test(e2e): convert the favorites, availability and orders specs (#137)
favorites, favorites-filter, sold-filter, orders and pending-publish. Five more copies of "register a customer" and three more of the hardcoded base URL go with them. Two locators that were hiding real knowledge are now named. `gridCell` is the item's whole antd column rather than its card, needed because the SOLD ribbon renders outside the card — three specs reached for `.ant-col` directly to get at it. And `chooseAvailability` goes through the title attribute because antd's Segmented hides the real radio behind a styled label, so the input is found by role and cannot be clicked; that fact was written out twice in comments and is now written once in code. The favorite control is located page-wide rather than within a card. The storefront paginates as items accumulate and the control is named for its item anyway, so scoping to a card bought nothing and broke whenever the card was on another page. favorites-filter keeps its local `favorite()` helper. It is genuinely local — decline the opt-in, wait for the fading modal to stop intercepting pointer events, confirm the heart flipped — and belongs to that file's subject rather than to the storefront. It now takes page objects as parameters instead of reaching for locators itself, which is what a spec-level helper should look like. Two specs still drive the registration form rather than taking the `customer` fixture, and deliberately. Both are about a signed-out visitor being interrupted mid-action — favoriting an item, or switching on the favorites filter — and the claim is that the thing they asked for survives the interruption. Replacing the interruption with an API call would delete the test. Verified: favorites 6/6, favorites-filter 7/7, sold-filter 6/6, orders and pending-publish 8/8. Notably favorites-filter's "keeps showing a favorite after it sells" passes, which had been failing on a strict-mode violation from two items sharing a name across runs. Refs #137
This commit is contained in:
@@ -1,27 +1,8 @@
|
||||
import { test, expect, Page } from './fixtures';
|
||||
|
||||
const PASSWORD = 'supersecret123';
|
||||
|
||||
const uniqueEmail = () => `orders-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 7)}@example.com`;
|
||||
|
||||
// The generous wait is the same one the other account specs use: registration is
|
||||
// a bcrypt round-trip rather than a render, and runs past Playwright's 5s
|
||||
// default when the suite's workers all register at once.
|
||||
async function registerCustomer(page: Page): Promise<string> {
|
||||
const email = uniqueEmail();
|
||||
await page.goto('/register');
|
||||
await page.getByRole('textbox', { name: 'Email' }).fill(email);
|
||||
await page.getByRole('textbox', { name: 'First name' }).fill('Test');
|
||||
await page.getByRole('textbox', { name: 'Last name' }).fill('Customer');
|
||||
await page.getByLabel('Password').fill(PASSWORD);
|
||||
await page.getByRole('button', { name: 'Create account' }).click();
|
||||
await expect(page.getByRole('button', { name: 'My Account' })).toBeVisible({ timeout: 20000 });
|
||||
return email;
|
||||
}
|
||||
import { test, expect } from './fixtures';
|
||||
|
||||
test.describe('Order history has a page of its own', () => {
|
||||
test('a signed-out visitor is sent to sign in', async ({ page }) => {
|
||||
await page.goto('/orders');
|
||||
test('a signed-out visitor is sent to sign in', async ({ page, orders }) => {
|
||||
await orders.goto();
|
||||
|
||||
await expect(page).toHaveURL(/\/login/, { timeout: 20000 });
|
||||
});
|
||||
@@ -29,43 +10,46 @@ test.describe('Order history has a page of its own', () => {
|
||||
// A page, not a modal: no dialog, and the storefront is not rendered behind
|
||||
// it. Putting /orders in MODAL_ROUTES would quietly undo the whole change,
|
||||
// and this is what would catch it.
|
||||
test('renders as a page rather than a modal over the storefront', async ({ page }) => {
|
||||
await registerCustomer(page);
|
||||
await page.goto('/orders');
|
||||
test('renders as a page rather than a modal over the storefront', async ({
|
||||
customer,
|
||||
orders,
|
||||
header
|
||||
}) => {
|
||||
await orders.goto();
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Order History' })).toBeVisible();
|
||||
await expect(page.getByRole('dialog')).toHaveCount(0);
|
||||
await expect(page.getByRole('heading', { name: 'Redefined Designs' })).toBeHidden();
|
||||
await expect(orders.heading).toBeVisible({ timeout: 20000 });
|
||||
await expect(orders.anyDialog).toHaveCount(0);
|
||||
await expect(header.siteTitle).toBeHidden();
|
||||
});
|
||||
|
||||
test('says so when there are no orders, rather than showing an empty table', async ({ page }) => {
|
||||
await registerCustomer(page);
|
||||
await page.goto('/orders');
|
||||
test('says so when there are no orders, rather than showing an empty table', async ({
|
||||
customer,
|
||||
orders
|
||||
}) => {
|
||||
await orders.goto();
|
||||
|
||||
await expect(page.getByText('No orders yet')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Continue Shopping' })).toBeVisible();
|
||||
await expect(orders.noOrdersNotice).toBeVisible({ timeout: 20000 });
|
||||
await expect(orders.continueShoppingButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('Back to Shop returns to the storefront', async ({ page }) => {
|
||||
await registerCustomer(page);
|
||||
await page.goto('/orders');
|
||||
test('Back to Shop returns to the storefront', async ({ page, customer, orders, header }) => {
|
||||
await orders.goto();
|
||||
await expect(orders.heading).toBeVisible({ timeout: 20000 });
|
||||
|
||||
await page.getByRole('button', { name: 'Back to Shop' }).click();
|
||||
await orders.backToShopButton.click();
|
||||
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
await expect(page.getByRole('heading', { name: 'Redefined Designs' })).toBeVisible();
|
||||
await expect(header.siteTitle).toBeVisible();
|
||||
});
|
||||
|
||||
// The account view is where a customer looks for their orders, so the route
|
||||
// out of it is the part that has to keep working now the table has gone.
|
||||
test('My Account links to it', async ({ page }) => {
|
||||
await registerCustomer(page);
|
||||
await page.goto('/account');
|
||||
test('My Account links to it', async ({ page, customer, accountModal, orders }) => {
|
||||
await accountModal.open();
|
||||
|
||||
await page.getByRole('dialog', { name: 'My Account' })
|
||||
.getByRole('button', { name: 'View order history' }).click();
|
||||
await accountModal.orderHistoryButton.click();
|
||||
|
||||
await expect(page).toHaveURL(/\/orders/);
|
||||
await expect(page.getByRole('heading', { name: 'Order History' })).toBeVisible();
|
||||
await expect(orders.heading).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user