0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor(test): add cls and dcls util functions (#4513)

To build string for matching class names.
This commit is contained in:
Gao Sun 2023-09-15 18:20:51 +08:00 committed by GitHub
parent c4e4b83740
commit 9eb512b5a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 6 deletions

View file

@ -3,6 +3,7 @@ import path from 'node:path';
import { appendPath } from '@silverhand/essentials'; import { appendPath } from '@silverhand/essentials';
import { consolePassword, consoleUsername, logtoConsoleUrl } from '#src/constants.js'; import { consolePassword, consoleUsername, logtoConsoleUrl } from '#src/constants.js';
import { dcls } from '#src/utils.js';
import ExpectPage, { ExpectPageError } from './expect-page.js'; import ExpectPage, { ExpectPageError } from './expect-page.js';
import { expectConfirmModalAndAct, expectToSaveChanges } from './index.js'; import { expectConfirmModalAndAct, expectToSaveChanges } from './index.js';
@ -53,7 +54,7 @@ export default class ExpectConsole extends ExpectPage {
async gotoPage(pathname: string, title: ConsoleTitle) { async gotoPage(pathname: string, title: ConsoleTitle) {
await this.navigateTo(this.buildUrl(path.join(this.options.tenantId, pathname))); await this.navigateTo(this.buildUrl(path.join(this.options.tenantId, pathname)));
await expect(this.page).toMatchElement( await expect(this.page).toMatchElement(
'div[class*=_main] div[class*=_container] div[class*=_cardTitle]', [dcls('main'), dcls('container'), dcls('cardTitle')].join(' '),
{ text: title } { text: title }
); );
} }
@ -67,7 +68,7 @@ export default class ExpectConsole extends ExpectPage {
await Promise.all( await Promise.all(
titles.map(async (title) => { titles.map(async (title) => {
return expect(this.page).toMatchElement( return expect(this.page).toMatchElement(
'div[class*=_tabContent] div[class*=_card] div[class*=_title]', [dcls('tabContent'), dcls('card'), dcls('title')].join(' '),
{ text: new RegExp(title, 'i'), visible: true } { text: new RegExp(title, 'i'), visible: true }
); );
}) })
@ -78,7 +79,7 @@ export default class ExpectConsole extends ExpectPage {
const fieldTitle = await expect(this.page).toMatchElement( const fieldTitle = await expect(this.page).toMatchElement(
// Use `:has()` for a quick and dirty way to match the field title. // Use `:has()` for a quick and dirty way to match the field title.
// Not harmful in most cases. // Not harmful in most cases.
'div[class*=_field]:has(div[class*=_title])', `${dcls('field')}:has(${dcls('title')})`,
{ {
text: new RegExp(title, 'i'), text: new RegExp(title, 'i'),
visible: true, visible: true,
@ -100,7 +101,9 @@ export default class ExpectConsole extends ExpectPage {
* Click a `<nav>` navigation tab (not the page tab) in the Console. * Click a `<nav>` navigation tab (not the page tab) in the Console.
*/ */
async toClickTab(tabName: string | RegExp) { async toClickTab(tabName: string | RegExp) {
await expect(this.page).toClick(`nav div[class*=_item] div[class*=_link] a`, { text: tabName }); await expect(this.page).toClick(['nav', dcls('item'), dcls('link'), 'a'].join(' '), {
text: tabName,
});
} }
async toSaveChanges(confirmation?: string | RegExp) { async toSaveChanges(confirmation?: string | RegExp) {

View file

@ -1,7 +1,7 @@
import { condString } from '@silverhand/essentials'; import { condString } from '@silverhand/essentials';
import { type ElementHandle, type Page } from 'puppeteer'; import { type ElementHandle, type Page } from 'puppeteer';
import { expectNavigation } from '#src/utils.js'; import { cls, dcls, expectNavigation } from '#src/utils.js';
/** Error thrown by {@link ExpectPage}. */ /** Error thrown by {@link ExpectPage}. */
export class ExpectPageError extends Error { export class ExpectPageError extends Error {
@ -141,7 +141,7 @@ export default class ExpectPage {
*/ */
async waitForToast(text: string | RegExp, type?: 'success' | 'error') { async waitForToast(text: string | RegExp, type?: 'success' | 'error') {
const toast = await expect(this.page).toMatchElement( const toast = await expect(this.page).toMatchElement(
`[class*=toast]${condString(type && `[class*=${type}]`)}:has(div[class$=message]`, `${cls('toast')}${condString(type && cls(type))}:has(${dcls('message')})`,
{ {
text, text,
} }

View file

@ -81,3 +81,27 @@ export const expectNavigation = async <T>(
]); ]);
return result; return result;
}; };
/**
* Build the string for a CSS selector that matches a class name.
*
* Since we use CSS modules, the class names are prefixed with a hash followed by a `_`.
* For example, the class name `foo` will be transformed to `abc123_foo`. This function
* returns a selector that matches any class name that contains `_foo`.
*
* It is accurate enough for our tests, as long as our class names are camelCased.
*/
export const cls = <C extends string>(className: C) => `[class*=_${className}]` as const;
/**
* Build the string for a CSS selector that matches a class name for a `<div>` element.
* This is a shorthand for `div${cls(className)}`.
*
* @example
* ```ts
* dcls('foo') // => 'div[class*=_foo]'
* ```
*
* @see {@link cls}
*/
export const dcls = <C extends string>(className: C) => `div${cls(className)}` as const;