Merge branch 'main' into fix/google-button-missing-on-signup
This commit is contained in:
@@ -25,14 +25,18 @@ test.describe('Editing the customer emails', () => {
|
|||||||
'Favorited item sold',
|
'Favorited item sold',
|
||||||
'Favorited item withdrawn',
|
'Favorited item withdrawn',
|
||||||
'Cart reminder',
|
'Cart reminder',
|
||||||
'Email address changed'
|
'Email address changed',
|
||||||
|
// Added in #337, and the reason these are matched on the whole
|
||||||
|
// accessible name rather than as substrings: it extends the label above
|
||||||
|
// it, so an unanchored match resolved to both tabs.
|
||||||
|
'Email address changed by the shop'
|
||||||
]) {
|
]) {
|
||||||
await expect(adminEmails.railTab(new RegExp(label))).toBeVisible();
|
await expect(adminEmails.railTab(label)).toBeVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only a customised template is marked, so which ones have been changed is
|
// Only a customised template is marked, so which ones have been changed is
|
||||||
// visible without opening each one. An untouched template carries nothing.
|
// visible without opening each one. An untouched template carries nothing.
|
||||||
await expect(adminEmails.railTab(/Password reset/)).toBeVisible();
|
await expect(adminEmails.railTab('Password reset')).toBeVisible();
|
||||||
await expect(adminEmails.customisedTab('Password reset')).toHaveCount(0);
|
await expect(adminEmails.customisedTab('Password reset')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
import { FrameLocator, Locator, Page, expect } from '@playwright/test';
|
import { FrameLocator, Locator, Page, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A matcher for one template label, against a tab's *whole* accessible name.
|
||||||
|
*
|
||||||
|
* A tab is named for its template, plus the word "Customised" once it has been
|
||||||
|
* edited — the dot beside it carries that as an aria-label, so the state is not
|
||||||
|
* colour-only.
|
||||||
|
*
|
||||||
|
* Anchored at both ends, which is the entire point of this function. The
|
||||||
|
* locators here used to build an unanchored regex from the label, so a template
|
||||||
|
* whose name merely *began* with another's matched both. Adding "Email address
|
||||||
|
* changed by the shop" alongside "Email address changed" broke a passing test
|
||||||
|
* with a strict-mode violation naming the assertion rather than the new
|
||||||
|
* template — the same shape as the switch locator that silently retargeted in
|
||||||
|
* #317, and the same cost to diagnose.
|
||||||
|
*
|
||||||
|
* The escape matters for the same reason: these labels are copy, and copy
|
||||||
|
* acquires brackets and full stops eventually.
|
||||||
|
*/
|
||||||
|
function nameMatching(label: string, options: { customised?: boolean } = {}): RegExp {
|
||||||
|
const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
const suffix = options.customised ? '\\s+Customised' : '(?:\\s+Customised)?';
|
||||||
|
return new RegExp(`^${escaped}${suffix}$`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Emails tab: a vertical rail of template types and one editor at a time.
|
* The Emails tab: a vertical rail of template types and one editor at a time.
|
||||||
*
|
*
|
||||||
@@ -30,13 +54,30 @@ export class AdminEmails {
|
|||||||
return this.page.getByRole('button', { name: `Insert {{${name}}}` });
|
return this.page.getByRole('button', { name: `Insert {{${name}}}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** One template's entry in the rail. */
|
/**
|
||||||
railTab(label: string | RegExp): Locator {
|
* One template's entry in the rail, matched on its whole accessible name.
|
||||||
return this.page.getByRole('tab', { name: label });
|
*
|
||||||
|
* A tab's accessible name is the template's label, plus the word "Customised"
|
||||||
|
* when it has been edited — the dot beside it carries that as an aria-label so
|
||||||
|
* the state is not colour-only.
|
||||||
|
*
|
||||||
|
* Anchored at both ends, which is the point of this helper rather than a bare
|
||||||
|
* substring match. These locators used to build an unanchored regex from the
|
||||||
|
* label, so a template whose name merely *began* with another's matched both.
|
||||||
|
* Adding "Email address changed by the shop" beside "Email address changed"
|
||||||
|
* broke a passing test with a strict-mode violation, and the failure named the
|
||||||
|
* assertion rather than the new template — the same shape as the switch
|
||||||
|
* locator that silently retargeted in #317.
|
||||||
|
*
|
||||||
|
* The escape matters for the same reason: a label is copy, and copy acquires
|
||||||
|
* brackets and full stops eventually.
|
||||||
|
*/
|
||||||
|
railTab(label: string): Locator {
|
||||||
|
return this.page.getByRole('tab', { name: nameMatching(label) });
|
||||||
}
|
}
|
||||||
|
|
||||||
customisedTab(label: string): Locator {
|
customisedTab(label: string): Locator {
|
||||||
return this.page.getByRole('tab', { name: new RegExp(`${label}.*Customised`) });
|
return this.page.getByRole('tab', { name: nameMatching(label, { customised: true }) });
|
||||||
}
|
}
|
||||||
|
|
||||||
subject(label: string): Locator {
|
subject(label: string): Locator {
|
||||||
@@ -65,7 +106,7 @@ export class AdminEmails {
|
|||||||
* resolved mid-swap finds the outgoing one.
|
* resolved mid-swap finds the outgoing one.
|
||||||
*/
|
*/
|
||||||
async openTemplate(label: string): Promise<void> {
|
async openTemplate(label: string): Promise<void> {
|
||||||
await this.railTab(new RegExp(label)).click();
|
await this.railTab(label).click();
|
||||||
await expect(this.subject(label)).toBeVisible();
|
await expect(this.subject(label)).toBeVisible();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user