diff --git a/.gitignore b/.gitignore index a47fed4b1..c0fd53417 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,5 @@ testem.log # System Files .DS_Store Thumbs.db + +/fusion-e2e/dist diff --git a/projects/fusion-e2e/.gitignore b/projects/fusion-e2e/.gitignore index 86c7cc8b7..d3110b643 100644 --- a/projects/fusion-e2e/.gitignore +++ b/projects/fusion-e2e/.gitignore @@ -4,3 +4,4 @@ node_modules/ /playwright-report/ /blob-report/ /playwright/.cache/ +dist diff --git a/projects/fusion-e2e/entities/behavior/behavior-class/base-element.ts b/projects/fusion-e2e/entities/behavior/behavior-class/base-element.ts new file mode 100644 index 000000000..f911202d3 --- /dev/null +++ b/projects/fusion-e2e/entities/behavior/behavior-class/base-element.ts @@ -0,0 +1,204 @@ +import {Locator, Page, test} from '@playwright/test'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export class BaseElement { + readonly page: Page; + readonly selector: string; + locator: Locator; + + constructor(page: Page, selector: string) { + this.page = page; + this.selector = selector; + this.locator = this.page.locator(selector); + } + + async click(): Promise { + await test.step(`Click on: ${this.selector}`, async () => { + await this.page.click(this.selector); + }); + } + + async count(): Promise { + let count: number; + + await test.step(`Count: ${this.selector}`, async () => { + count = await this.page.locator(this.selector).count(); + }); + + return count; + } + + async hover(): Promise { + await test.step(`Hover on: ${this.selector}`, async () => { + await this.page.hover(this.selector); + }); + } + + async isVisible(): Promise { + let isVisible: boolean; + + await test.step(`Is visible: ${this.selector}`, async () => { + isVisible = await this.page.isVisible(this.selector); + }); + + return isVisible; + } + + async isHidden(): Promise { + let isHidden: boolean; + + await test.step(`Is hidden: ${this.selector}`, async () => { + isHidden = await this.page.isHidden(this.selector); + }); + + return isHidden; + } + + async isDisabled(): Promise { + let isDisabled: boolean; + + await test.step(`Is disabled: ${this.selector}`, async () => { + isDisabled = await this.page.isDisabled(this.selector); + }); + + return isDisabled; + } + + async isEnabled(): Promise { + let isEnabled: boolean; + + await test.step(`Is enabled: ${this.selector}`, async () => { + isEnabled = await this.page.isEnabled(this.selector); + }); + + return isEnabled; + } + + async isAttached(): Promise { + let isAttached: any; + + await test.step(`Is attached: ${this.selector}`, async () => { + isAttached = await this.page.waitForSelector(this.selector, {state: `attached`}); + }); + + return isAttached; + } + + async getSelector(): Promise { + let selector: string; + + await test.step(`Get selector`, async () => { + selector = this.selector; + }); + + return selector; + } + + async getLocator(selector: string): Promise { + let locator: Locator; + + await test.step(`Get locator of: ${this.selector}`, async () => { + locator = this.page.locator(selector); + }); + + return locator; + } + + async getAttribute(attributeName: string): Promise { + let attribute: string | null; + + await test.step(`Get attribute of: ${attributeName}`, async () => { + attribute = await this.page.getAttribute(this.selector, attributeName); + }); + return attribute; + } + + async keyPress(key: string, count: number = 1): Promise { + await test.step(`Press key: ${key}, ${count} times`, async () => { + for (let i = 0; i < count; i++) { + await this.page.keyboard.press(key); + } + }); + } + + async waitForElementToBeAttached(): Promise { + await test.step(`Wait for element to be attached: ${this.selector}`, async () => { + await this.locator.waitFor({state: `attached`}); + }); + } + + async waitForTimeout(timeout: number): Promise { + await test.step(`Wait for timeout`, async () => { + await this.page.waitForTimeout(timeout); + }); + } + + async waitForURL(url: string): Promise { + await test.step(`Wait for URL: ${url}`, async () => { + await this.page.waitForURL(url); + }); + } + + async waitForLoadState(state: `load` | `domcontentloaded` | `networkidle` = `load`) { + await test.step(`Wait for load state: ${state}`, async () => { + await this.page.waitForLoadState(state); + }); + } + + async waitForSelector(selectorName: string): Promise { + await test.step(`Wait for selector: ${selectorName}`, async () => { + await this.page.waitForSelector(selectorName); + }); + } + + async waitForComponent(modifiers?: string) { + let loadedPageSelector: string; + if (!modifiers) { + loadedPageSelector = getTestIdSelector(this.selector); + } else { + loadedPageSelector = getTestIdSelector(getTestId(this.selector, modifiers)); + } + await this.waitForSelector(loadedPageSelector); + } + + async selectorText(locator: Locator): Promise { + let text: string; + + await test.step(`Get text of: ${this.selector}`, async () => { + text = await locator.textContent(); + }); + + return text; + } + + async textContent(): Promise { + let text: string; + + await test.step(`Get text content of: ${this.selector}`, async () => { + text = await this.locator.textContent(); + }); + + return text; + } + + async reload(): Promise { + await test.step(`Reload page`, async () => { + await this.page.reload(); + }); + } + + async getByTestId(testId: string): Promise { + return test.step(`Get element by test id: ${testId}`, async () => { + return this.page.getByTestId(testId); + }); + } + + async waitForEvent(event: any): Promise { + let eventResponse: any; + await test.step(`Wait for event ${event}`, async () => { + eventResponse = this.page.waitForEvent(event); + }); + + return eventResponse; + } +} diff --git a/projects/fusion-e2e/entities/behavior/behavior-class/checkable.ts b/projects/fusion-e2e/entities/behavior/behavior-class/checkable.ts new file mode 100644 index 000000000..198abdc3f --- /dev/null +++ b/projects/fusion-e2e/entities/behavior/behavior-class/checkable.ts @@ -0,0 +1,35 @@ +import {Page, test} from '@playwright/test'; +import {BaseElement} from './base-element'; + +export class Checkable extends BaseElement { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async check(): Promise { + await test.step(`Check`, async () => { + await this.page.check(this.selector); + if (!(await this.isChecked())) { + throw new Error(`Couldn't check the Check box ${this.selector}`); + } + }); + } + async uncheck(): Promise { + await test.step(`UnCheck`, async () => { + await this.page.uncheck(this.selector); + if (await this.isChecked()) { + throw new Error(`Couldn't uncheck the Check box ${this.selector}`); + } + }); + } + + async isChecked(): Promise { + let isChecked: boolean; + + await test.step(`isChecked`, async () => { + isChecked = await this.locator.isChecked(); + }); + + return isChecked; + } +} diff --git a/projects/fusion-e2e/entities/behavior/behavior-class/clickable.ts b/projects/fusion-e2e/entities/behavior/behavior-class/clickable.ts new file mode 100644 index 000000000..abd4aea96 --- /dev/null +++ b/projects/fusion-e2e/entities/behavior/behavior-class/clickable.ts @@ -0,0 +1,8 @@ +import {Page} from '@playwright/test'; +import {BaseElement} from './base-element'; + +export class Clickable extends BaseElement { + constructor(page: Page, selector: string) { + super(page, selector); + } +} diff --git a/projects/fusion-e2e/entities/behavior/behavior-class/editable.ts b/projects/fusion-e2e/entities/behavior/behavior-class/editable.ts new file mode 100644 index 000000000..2dd4a0ff0 --- /dev/null +++ b/projects/fusion-e2e/entities/behavior/behavior-class/editable.ts @@ -0,0 +1,28 @@ +import {Page, test} from '@playwright/test'; +import {Clickable} from './clickable'; + +export class Editable extends Clickable { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async clearInput(): Promise { + await test.step(`Clear input`, async () => { + await this.page.fill(this.selector, ''); + }); + } + + async fill(text: string): Promise { + await test.step(`Fill input with text: ${text}`, async () => { + await this.page.fill(this.selector, ''); + await this.page.fill(this.selector, text); + }); + } + + async type(text: string): Promise { + await test.step(`Fill input with text: ${text}`, async () => { + await this.page.fill(this.selector, ''); + await this.locator.type(text, {delay: 50}); + }); + } +} diff --git a/projects/fusion-e2e/entities/behavior/index.ts b/projects/fusion-e2e/entities/behavior/index.ts new file mode 100644 index 000000000..f4f85e2d7 --- /dev/null +++ b/projects/fusion-e2e/entities/behavior/index.ts @@ -0,0 +1,4 @@ +export {Editable} from './behavior-class/editable'; +export {Clickable} from './behavior-class/clickable'; +export {Checkable} from './behavior-class/checkable'; +export {BaseElement} from './behavior-class/base-element'; diff --git a/projects/fusion-e2e/entities/components/alert/alert-component.ts b/projects/fusion-e2e/entities/components/alert/alert-component.ts new file mode 100644 index 000000000..606f08b99 --- /dev/null +++ b/projects/fusion-e2e/entities/components/alert/alert-component.ts @@ -0,0 +1,55 @@ +import {AlertTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; +import {BaseComponent} from '../base-component'; +import {ButtonComponent} from '../button/button-component'; +import {StaticText} from '../../elements'; +import {Page} from '@playwright/test'; + +export class AlertComponent extends BaseComponent { + wrapperElement: StaticText; + messageElement: StaticText; + titleElement: StaticText; + actionButtonElement: ButtonComponent; + closeButtonElement: ButtonComponent; + + constructor(page: Page, selector: string) { + super(page, selector); + this.wrapperElement = new StaticText(page, getTestIdSelector(getTestId(this.selector, AlertTestIdModifiers.WRAPPER))); + this.messageElement = new StaticText(page, getTestIdSelector(getTestId(this.selector, AlertTestIdModifiers.MESSAGE))); + this.titleElement = new StaticText(page, getTestIdSelector(getTestId(this.selector, AlertTestIdModifiers.TITLE))); + this.actionButtonElement = new ButtonComponent( + page, + getTestIdSelector(getTestId(this.selector, AlertTestIdModifiers.ACTION_BUTTON)) + ); + this.closeButtonElement = new ButtonComponent(page, getTestIdSelector(getTestId(this.selector, AlertTestIdModifiers.CLOSE_BUTTON))); + } + + async getAlertText(): Promise { + return this.messageElement.textContent(); + } + + async getAlertTitle(): Promise { + return this.titleElement.textContent(); + } + + async getActionButtonText(): Promise { + return this.actionButtonElement.textContent(); + } + + async clickOnActionButton(): Promise { + await this.actionButtonElement.click(); + } + + async closeAlert(): Promise { + await this.closeButtonElement.click(); + } + + async isAlertVisible(): Promise { + const alertSelector = await this.wrapperElement.count(); + return alertSelector > 0; + } + + async getAlertIconType(): Promise { + return this.wrapperElement.getAttribute('class'); + } +} diff --git a/projects/fusion-e2e/tests/components/alert/consts.ts b/projects/fusion-e2e/entities/components/alert/const.ts similarity index 65% rename from projects/fusion-e2e/tests/components/alert/consts.ts rename to projects/fusion-e2e/entities/components/alert/const.ts index 4c497f4d2..ad19ed130 100644 --- a/projects/fusion-e2e/tests/components/alert/consts.ts +++ b/projects/fusion-e2e/entities/components/alert/const.ts @@ -1,14 +1,7 @@ -import {AlertTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {AlertTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../global/utils'; export const alertStoryId = 'v4-components-feedback-alert--basic'; export const alertSeveritiesStoryId = 'v4-components-feedback-alert--severities'; export const defaultTestId = 'alert-default'; -export const successTestId = 'success-alert'; -export const warningTestId = 'warning-alert'; - -export const dangerTestId = 'danger-alert'; -export const infoTestId = 'info-alert'; -export const outlinedTestId = 'outlined-alert'; - export const loadedPageSelector = getTestIdSelector(getTestId(defaultTestId, AlertTestIdModifiers.WRAPPER)); diff --git a/projects/fusion-e2e/entities/components/base-component.ts b/projects/fusion-e2e/entities/components/base-component.ts new file mode 100644 index 000000000..7e1bdda20 --- /dev/null +++ b/projects/fusion-e2e/entities/components/base-component.ts @@ -0,0 +1,20 @@ +import {Page} from '@playwright/test'; +import {BaseElement} from '../behavior'; + +export class BaseComponent extends BaseElement { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async isAllControlsVisible(controls: BaseElement[]) { + let isVisible = true; + for (const control of controls) { + const isControlVisible = await control.isVisible(); + if (!isControlVisible) { + isVisible = false; + break; + } + } + return isVisible; + } +} diff --git a/projects/fusion-e2e/entities/components/button/button-component.ts b/projects/fusion-e2e/entities/components/button/button-component.ts new file mode 100644 index 000000000..293a7839a --- /dev/null +++ b/projects/fusion-e2e/entities/components/button/button-component.ts @@ -0,0 +1,85 @@ +import {getTestId, getTestIdSelector} from '../../global/utils'; +import {ButtonTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseElement, Clickable} from '../../behavior'; +import {StaticText} from '../../elements'; +import {test} from '@playwright/test'; +import {BaseComponent} from '../base-component'; + +export class ButtonComponent extends BaseComponent { + protected button: Clickable; + protected label: StaticText; + contentElement: BaseElement; + buttonModifierElement: BaseElement; + buttonElement: BaseElement; + + constructor(page, selector: string) { + super(page, selector); + this.contentElement = new BaseElement(page, getTestIdSelector(getTestId(this.selector, ButtonTestIdModifiers.CONTENT))); + this.buttonModifierElement = new BaseElement(page, getTestIdSelector(getTestId(this.selector, ButtonTestIdModifiers.BUTTON))); + this.buttonElement = new BaseElement(page, getTestIdSelector(this.selector)); + this.button = new Clickable(page, selector); + this.label = new StaticText(page, selector); + } + + async waitForToggleButtonComponent() { + const loadedPageSelector = getTestIdSelector(this.selector); + await this.waitForSelector(loadedPageSelector); + } + + async clickOnButton() { + await this.buttonModifierElement.click(); + } + + async hoverOnButton() { + await this.buttonModifierElement.hover(); + } + + async isButtonLoading() { + const buttonLocator = await this.getLocator(getTestIdSelector(this.selector)); + const buttonElement = await buttonLocator.elementHandle(); + const buttonClass = await buttonElement.getAttribute('class'); + return buttonClass.includes('loading'); + } + + async getButtonText() { + return this.contentElement.textContent(); + } + + async getIconButtonText() { + const button = await this.getLocator(getTestIdSelector(this.selector)); + const textSelector = await button.last().locator('span'); + return this.selectorText(textSelector); + } + + async isButtonDisabled() { + return this.buttonElement.isDisabled(); + } + + async getToggleButtonFirstLabel() { + return this.buttonElement.textContent(); + } + + async isIconExist(selector: string): Promise { + let numOfIcons: number; + + await test.step(`Is icon exist: ${selector}`, async () => { + const isButtonWithIcon = await this.getLocator(selector); + numOfIcons = await isButtonWithIcon.count(); + }); + + return numOfIcons > 0; + } + + async getButtonLabel(): Promise { + let labelSelector: string; + + await test.step(`Get button label`, async () => { + labelSelector = await this.label.getText(); + if (labelSelector === null) { + throw new Error(`Couldn't get text for the button: ${this.selector}`); + } + }); + + return labelSelector; + } +} diff --git a/projects/fusion-e2e/tests/components/button/consts.ts b/projects/fusion-e2e/entities/components/button/consts.ts similarity index 100% rename from projects/fusion-e2e/tests/components/button/consts.ts rename to projects/fusion-e2e/entities/components/button/consts.ts diff --git a/projects/fusion-e2e/entities/components/chart/chart-component.ts b/projects/fusion-e2e/entities/components/chart/chart-component.ts new file mode 100644 index 000000000..94f71c108 --- /dev/null +++ b/projects/fusion-e2e/entities/components/chart/chart-component.ts @@ -0,0 +1,39 @@ +import {BaseComponent} from '../base-component'; +import {getTestId} from '../../global/utils'; +import {ChartLabelTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; + +import {Locator} from '@playwright/test'; + +export class ChartComponent extends BaseComponent { + private labels = this.getByTestId(getTestId(this.selector, ChartLabelTestIdModifiers.LABEL)); + private iconModifier = ChartLabelTestIdModifiers.LABEL_ICON; + private colorModifier = ChartLabelTestIdModifiers.LABEL_COLOR; + + constructor(page, selector: string) { + super(page, selector); + } + + async getLabelText({testId}: {testId: string}) { + let label = await this.getByTestId(testId); + return this.selectorText(label); + } + + async getIcon(label: Locator) { + const iconAttribute: Locator = label.getByTestId(getTestId(this.selector, this.iconModifier)); + return await iconAttribute.getAttribute('ng-reflect-name'); + } + + async getColor(label: Locator) { + const colorAttribute: Locator = label.getByTestId(getTestId(this.selector, this.colorModifier)); + const style = await colorAttribute.getAttribute('style'); + return style + .split(';') + .find(style => style.trim().startsWith('background-color')) + .split(':')[1] + .trim(); + } + + async getLabels() { + return await this.labels; + } +} diff --git a/projects/fusion-e2e/tests/components/chart/consts.ts b/projects/fusion-e2e/entities/components/chart/consts.ts similarity index 73% rename from projects/fusion-e2e/tests/components/chart/consts.ts rename to projects/fusion-e2e/entities/components/chart/consts.ts index a1b95654e..b0ccaa597 100644 --- a/projects/fusion-e2e/tests/components/chart/consts.ts +++ b/projects/fusion-e2e/entities/components/chart/consts.ts @@ -1,9 +1,9 @@ -import {ChartLabelTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {ChartLabelTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../global/utils'; -export const chartStoryId = 'v4-components-datavisualization-charts-barchart--basic'; +export const chartStoryId = 'v4-components-datavisualization-charts-barchart--stack'; export const defaultTestId = 'charts-default'; export const labelTestId = getTestId(defaultTestId, ChartLabelTestIdModifiers.LABEL); -export const loadedPageSelector = getTestIdSelector(labelTestId); +export const loadedPageSelector = getTestIdSelector(defaultTestId); diff --git a/projects/fusion-e2e/entities/components/chip-filter/chipFilter-component.ts b/projects/fusion-e2e/entities/components/chip-filter/chipFilter-component.ts new file mode 100644 index 000000000..1bc70410f --- /dev/null +++ b/projects/fusion-e2e/entities/components/chip-filter/chipFilter-component.ts @@ -0,0 +1,26 @@ +import {ChipFilterTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; +import {BaseComponent} from '../base-component'; +import {StaticText} from '../../elements'; +import {Page} from '@playwright/test'; +import {Clickable} from '../../behavior'; + +export class ChipFilterComponent extends BaseComponent { + chipFilterElement: Clickable; + chipFilterLabel: StaticText; + + constructor(page: Page, selector: string) { + super(page, selector); + const chipFilterSelector = getTestIdSelector(getTestId(selector, ChipFilterTestIdModifiers.CHIP_FILTER)); + this.chipFilterElement = new Clickable(page, chipFilterSelector); + this.chipFilterLabel = new StaticText(page, `${chipFilterSelector} .fu-chip-label`); + } + + async getChipFilterLabel() { + return this.chipFilterLabel.getText(); + } + + async clickChipFilter() { + return this.chipFilterElement.click(); + } +} diff --git a/projects/fusion-e2e/tests/components/chip-filter/consts.ts b/projects/fusion-e2e/entities/components/chip-filter/consts.ts similarity index 94% rename from projects/fusion-e2e/tests/components/chip-filter/consts.ts rename to projects/fusion-e2e/entities/components/chip-filter/consts.ts index e13432c28..fae0588ac 100644 --- a/projects/fusion-e2e/tests/components/chip-filter/consts.ts +++ b/projects/fusion-e2e/entities/components/chip-filter/consts.ts @@ -1,4 +1,4 @@ -import {ChipFilterTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {ChipFilterTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../global/utils'; export const chipFilterStoryId = 'v4-components-filterpanel--default'; diff --git a/projects/fusion-e2e/tests/components/dialog/consts.ts b/projects/fusion-e2e/entities/components/dialog/consts.ts similarity index 95% rename from projects/fusion-e2e/tests/components/dialog/consts.ts rename to projects/fusion-e2e/entities/components/dialog/consts.ts index 225f3d3d1..15eaab999 100644 --- a/projects/fusion-e2e/tests/components/dialog/consts.ts +++ b/projects/fusion-e2e/entities/components/dialog/consts.ts @@ -1,4 +1,4 @@ -import {DialogTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {DialogTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../global/utils'; export const dialogStoryId = 'v4-components-feedback-dialog-dialog--basic'; diff --git a/projects/fusion-e2e/entities/components/dialog/dialog-component.ts b/projects/fusion-e2e/entities/components/dialog/dialog-component.ts new file mode 100644 index 000000000..37127cc6e --- /dev/null +++ b/projects/fusion-e2e/entities/components/dialog/dialog-component.ts @@ -0,0 +1,79 @@ +import {DialogTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; +import {BaseComponent} from '../base-component'; + +export class DialogComponent extends BaseComponent { + private headerModifier = DialogTestIdModifiers.HEADER; + private contentModifier = DialogTestIdModifiers.MODAL_CONTENT; + private wrapperModifier = DialogTestIdModifiers.WRAPPER; + private closeModifier = DialogTestIdModifiers.ACTION_CLOSE; + private saveButtonModifier = DialogTestIdModifiers.SAVE_BUTTON; + private cancelButtonModifier = DialogTestIdModifiers.CANCEL_BUTTON; + private actionButtonsWrapperModifier = DialogTestIdModifiers.ACTION_BUTTONS_WRAPPER; + private modalWrapperModifier = DialogTestIdModifiers.MODAL_WRAPPER; + + constructor(page, selector: string) { + super(page, selector); + } + + async getDialogElement(modifier: string) { + const testIdSelector = getTestIdSelector(getTestId(this.selector, modifier)); + return await this.getLocator(testIdSelector); + } + + // Get the title of the dialog + async getDialogTitle() { + const element = await this.getDialogElement(this.headerModifier); + return element.textContent(); + } + + // Get the text of the dialog + async getDialogText() { + const element = await this.getDialogElement(this.contentModifier); + return element.textContent(); + } + + // Open the dialog + async openDialog() { + const dialogButton = await this.getDialogElement(this.wrapperModifier); + await dialogButton.click(); + } + + // Close the dialog + async closeDialog() { + const closeButton = await this.getDialogElement(this.closeModifier); + await closeButton.click(); + } + + // Click on the primary button of the dialog + async clickOnPrimaryButton() { + const primaryButton = await this.getDialogElement(this.saveButtonModifier); + await primaryButton.click(); + } + + // Click on the default button of the dialog + async clickOnDefaultButton() { + const defaultButton = await this.getDialogElement(this.cancelButtonModifier); + await defaultButton.click(); + } + + // Click on the delete button of the dialog + async clickOnDeleteButton() { + const wrapperLocator = await this.getDialogElement(this.actionButtonsWrapperModifier); + const deleteButton = await wrapperLocator.locator('.danger'); + await deleteButton.click(); + } + + // Get the subtitle of the dialog + async getDialogSubtitle() { + const element = await this.getDialogElement(this.headerModifier); + const locator = await element.locator('.subtitle'); + return locator.textContent(); + } + + // Check if the dialog is visible + async isDialogVisible() { + const dialog = await this.getDialogElement(this.modalWrapperModifier); + return dialog.isVisible(); + } +} diff --git a/projects/fusion-e2e/entities/components/dropdown/base-dropdown.ts b/projects/fusion-e2e/entities/components/dropdown/base-dropdown.ts new file mode 100644 index 000000000..1d7881cd4 --- /dev/null +++ b/projects/fusion-e2e/entities/components/dropdown/base-dropdown.ts @@ -0,0 +1,72 @@ +import {getTestId} from '../../global/utils'; +import {SelectionByIndex, SelectionByName, SelectMultiple, SelectMultipleByName} from './types'; +import {DropdownTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; +import {Locator} from '@playwright/test'; + +export class BaseDropdownComponent extends BaseComponent { + constructor(page, selector: string) { + super(page, selector); + } + + async waitForComponent() { + await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.WRAPPER)); + } + + async selectDropdownOptionByIndex({index}: SelectionByIndex) { + await this.openDropdownComponent(); + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator: Locator = element.locator('fusion-dropdown-options-list > li').nth(index); + await locator.click(); + } + + async getDropdownButtonContent() { + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.BUTTON_CONTENT)); + return element.textContent(); + } + + async openDropdownComponent() { + const element = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.TRIGGER)); + await element.click({ + position: { + x: 15, + y: 15 + } + }); + } + + async selectDropdownOptionByName({name, shouldOpen = true}: SelectionByName) { + if (shouldOpen) await this.openDropdownComponent(); + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator: Locator = element.locator('fusion-dropdown-options-list > li', {hasText: name}); + await locator.click(); + } + + async selectMultipleItemsByIndex({itemsToSelect}: SelectMultiple) { + await this.openDropdownComponent(); + for (const i of itemsToSelect) { + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator: Locator = element.locator('ul > li').nth(i); + await locator.click(); + } + } + + async selectMultipleItemsByName({itemsToSelect}: SelectMultipleByName) { + await this.openDropdownComponent(); + for (const name of itemsToSelect) { + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator: Locator = element.locator('ul > li', {hasText: name}).first(); + await locator.click(); + } + } + + async clickOnApply() { + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.ACTION_APPLY)); + await element.click(); + } + + async clickOnCancel() { + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.ACTION_CANCEL)); + await element.click(); + } +} diff --git a/projects/fusion-e2e/tests/components/dropdown/consts.ts b/projects/fusion-e2e/entities/components/dropdown/consts.ts similarity index 100% rename from projects/fusion-e2e/tests/components/dropdown/consts.ts rename to projects/fusion-e2e/entities/components/dropdown/consts.ts diff --git a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/consts.ts b/projects/fusion-e2e/entities/components/dropdown/dropdown-v4/consts.ts similarity index 94% rename from projects/fusion-e2e/tests/components/dropdown/dropdown-v4/consts.ts rename to projects/fusion-e2e/entities/components/dropdown/dropdown-v4/consts.ts index cd4b8a025..ca0e43f29 100644 --- a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/consts.ts +++ b/projects/fusion-e2e/entities/components/dropdown/dropdown-v4/consts.ts @@ -1,4 +1,4 @@ -import {DropdownTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {DropdownTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../../global/utils'; export const dropdownDefaultStoryId = 'v4-components-dropdown-singleselection--basic'; diff --git a/projects/fusion-e2e/entities/components/dropdown/dropdown-v4/dropdown-component.ts b/projects/fusion-e2e/entities/components/dropdown/dropdown-v4/dropdown-component.ts new file mode 100644 index 000000000..bf8ed8319 --- /dev/null +++ b/projects/fusion-e2e/entities/components/dropdown/dropdown-v4/dropdown-component.ts @@ -0,0 +1,107 @@ +import {getTestId} from '../../../global/utils'; +import {BaseDropdownComponent} from '../base-dropdown'; +import {FieldLabelComponent} from '../../fieldLabel/field-label-component'; +import {FieldHelpTextComponent} from '../../fieldHelpText/field-help-text-component'; +import {HasHelpTextTypeParams} from '../../fieldHelpText/types'; +import { + DropdownTestIdModifiers, + FieldHelpTextTestIdModifiers, + InputTestIdModifiers +} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {Locator} from '@playwright/test'; + +type SearchItem = { + selector: string; + searchTerm: string; +}; + +export class DropdownComponent extends BaseDropdownComponent { + readonly fieldLabelComponent; + readonly fieldHelpTextComponent; + + constructor(page, selector: string) { + super(page, selector); + this.fieldLabelComponent = new FieldLabelComponent(page, selector); + this.fieldHelpTextComponent = new FieldHelpTextComponent(page, selector); + } + + getDropdownTitle() { + return this.fieldLabelComponent.getLabelText(); + } + + isMandatory() { + return this.fieldLabelComponent.isMandatory(); + } + + getHelpIconText() { + return this.fieldLabelComponent.getHelpIconText(); + } + + hasExtraText() { + return this.fieldHelpTextComponent.hasExtraText(); + } + + getExtraText() { + return this.fieldHelpTextComponent.getExtraText(); + } + + hasExtraTextIconType({type}: HasHelpTextTypeParams) { + return this.fieldHelpTextComponent.hasExtraTextIconType({testId: this.selector, type: type}); + } + + async getSelectedLabel() { + const element: Locator = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.BUTTON_CONTENT)); + return element.textContent(); + } + + async searchForItem({selector, searchTerm}: SearchItem) { + await this.openDropdownComponent(); + const inputElement = await (await this.getByTestId(getTestId(selector, InputTestIdModifiers.FIELD))).last(); + await inputElement.fill(searchTerm); + const listElement = await this.getByTestId(getTestId(selector, DropdownTestIdModifiers.LIST_CONTAINER)); + const firstItem = await listElement.locator('fusion-dropdown-options-list > li').first(); + return firstItem.textContent(); + } + + async isErrorText() { + const errorTextLocator = await this.getByTestId(getTestId(this.selector, FieldHelpTextTestIdModifiers.TEXT)); + const count = await errorTextLocator.count(); + return count > 0; + } + + async isDropdownDisabled() { + const ddTriggerSelector = await ( + await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.TRIGGER)) + ).locator('.button__container--disabled'); + + return ddTriggerSelector.isVisible(); + } + + async clearAllOptions() { + await (await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.ACTION_CLEAR_ALL))).click(); + } + + async isSelectAllChecked() { + const element = await ( + await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.SELECT_ALL)) + ).locator('.fu-label-checkbox'); + return element.isChecked(); + } + + async isSelectAllIndeterminate() { + const locator = await ( + await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.SELECT_ALL)) + ).locator('fusion-checkbox'); + return (await locator.getAttribute('ng-reflect-is-indeterminate')) === 'true'; + } + + async removeChipSelection() { + const element = await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.BUTTON_CLEAR)); + await element.click(); + } + + async getDropdownOptions() { + const element = await (await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.LIST_CONTAINER))).locator('.list'); + return element.allTextContents(); + } +} diff --git a/projects/fusion-e2e/tests/components/dropdown/types.ts b/projects/fusion-e2e/entities/components/dropdown/types.ts similarity index 100% rename from projects/fusion-e2e/tests/components/dropdown/types.ts rename to projects/fusion-e2e/entities/components/dropdown/types.ts diff --git a/projects/fusion-e2e/entities/components/fieldHelpText/field-help-text-component.ts b/projects/fusion-e2e/entities/components/fieldHelpText/field-help-text-component.ts new file mode 100644 index 000000000..14ff2dc08 --- /dev/null +++ b/projects/fusion-e2e/entities/components/fieldHelpText/field-help-text-component.ts @@ -0,0 +1,33 @@ +import {getTestId} from '../../global/utils'; +import {HasHelpTextTypeParams} from './types'; +import {FieldHelpTextTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; +import {Locator, Page} from '@playwright/test'; + +export class FieldHelpTextComponent extends BaseComponent { + constructor(page: Page, selector: string) { + super(page, selector); + } + + // Check if the extra text is visible + async hasExtraText(): Promise { + const testIdSelector = getTestId(this.selector, FieldHelpTextTestIdModifiers.TEXT); + const byTestId: Locator = await this.getByTestId(testIdSelector); + return (await byTestId).isVisible(); + } + + // Get the extra text + async getExtraText(): Promise { + const testIdSelector = getTestId(this.selector, FieldHelpTextTestIdModifiers.TEXT); + const byTestId: Locator = await this.getByTestId(testIdSelector); + return byTestId.textContent(); + } + + // Check if the extra text icon type is present + async hasExtraTextIconType({testId, type}: HasHelpTextTypeParams): Promise { + const testIdSelector = getTestId(testId, FieldHelpTextTestIdModifiers.CONTAINER); + const containerLocator: Locator = await this.getByTestId(testIdSelector); + const extraTextIconTypeLocator: Locator = await containerLocator.locator(`.icon.icon-name--${type}`); + return (await extraTextIconTypeLocator.count()) !== 0; + } +} diff --git a/projects/fusion-e2e/entities/components/fieldHelpText/types.ts b/projects/fusion-e2e/entities/components/fieldHelpText/types.ts new file mode 100644 index 000000000..b8cc5c038 --- /dev/null +++ b/projects/fusion-e2e/entities/components/fieldHelpText/types.ts @@ -0,0 +1,4 @@ +export type HasHelpTextTypeParams = { + testId?: string; + type: string; +}; diff --git a/projects/fusion-e2e/entities/components/fieldLabel/field-label-component.ts b/projects/fusion-e2e/entities/components/fieldLabel/field-label-component.ts new file mode 100644 index 000000000..92c585cdf --- /dev/null +++ b/projects/fusion-e2e/entities/components/fieldLabel/field-label-component.ts @@ -0,0 +1,23 @@ +import {getTestId} from '../../global/utils'; +import {FieldLabelTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; + +export class FieldLabelComponent extends BaseComponent { + constructor(page, selector: string) { + super(page, selector); + } + + async getLabelText() { + return (await this.getByTestId(getTestId(this.selector, FieldLabelTestIdModifiers.TEXT))).textContent(); + } + + async isMandatory() { + return (await this.getByTestId(getTestId(this.selector, FieldLabelTestIdModifiers.MANDATORY))).isVisible(); + } + + async getHelpIconText() { + const helpIconSelector = await this.getByTestId(getTestId(this.selector, FieldLabelTestIdModifiers.TOOLTIP)); + await helpIconSelector.hover(); + return helpIconSelector.getAttribute('text'); + } +} diff --git a/projects/fusion-e2e/entities/components/inputs/base-input.ts b/projects/fusion-e2e/entities/components/inputs/base-input.ts new file mode 100644 index 000000000..ba05bd1ff --- /dev/null +++ b/projects/fusion-e2e/entities/components/inputs/base-input.ts @@ -0,0 +1,43 @@ +import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {Page} from '@playwright/test'; +import {BaseElement, Editable} from '../../behavior'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export class BaseInputComponent extends Editable { + private inputElement: BaseElement; + + constructor(page: Page, selector: string) { + super(page, selector); + this.inputElement = new BaseElement(page, getTestIdSelector(getTestId(this.selector, InputTestIdModifiers.FIELD))); + } + + // Get the text of the input field + async getInputsFieldText(): Promise { + return this.inputElement.locator.inputValue(); + } + + // Get the placeholder text of the input + async getPlaceholderText(): Promise { + return this.inputElement.locator.getAttribute('placeholder'); + } + + // Get the type of the input + async getInputsType(): Promise { + return this.inputElement.locator.getAttribute('type'); + } + + // Add input to the input field + async addInput({text}): Promise { + await this.inputElement.locator.type(text as string); + } + + // Check if the input is disabled + async isInputDisabled(): Promise { + return this.inputElement.locator.isDisabled(); + } + + // Get the value of the input + async getInputValue(): Promise { + return this.locator.inputValue(); + } +} diff --git a/projects/fusion-e2e/tests/components/inputs/input-v4/consts.ts b/projects/fusion-e2e/entities/components/inputs/input-v4/consts.ts similarity index 97% rename from projects/fusion-e2e/tests/components/inputs/input-v4/consts.ts rename to projects/fusion-e2e/entities/components/inputs/input-v4/consts.ts index 908db83a5..24a06830a 100644 --- a/projects/fusion-e2e/tests/components/inputs/input-v4/consts.ts +++ b/projects/fusion-e2e/entities/components/inputs/input-v4/consts.ts @@ -1,4 +1,4 @@ -import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../../global/utils'; export const inputsStoryId = 'v4-components-inputs-textfield--default'; diff --git a/projects/fusion-e2e/entities/components/inputs/input-v4/inputs-component.ts b/projects/fusion-e2e/entities/components/inputs/input-v4/inputs-component.ts new file mode 100644 index 000000000..60d6ef53c --- /dev/null +++ b/projects/fusion-e2e/entities/components/inputs/input-v4/inputs-component.ts @@ -0,0 +1,129 @@ +import {FieldLabelComponent} from '../../fieldLabel/field-label-component'; +import {FieldHelpTextComponent} from '../../fieldHelpText/field-help-text-component'; +import {HasHelpTextTypeParams} from '../../fieldHelpText/types'; +import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../../global/utils'; +import {BaseInputComponent} from '../base-input'; +import {Locator, Page} from '@playwright/test'; +import {BaseElement} from '../../../behavior'; + +export class InputsComponent extends BaseInputComponent { + private wrapper: BaseElement; + private inputField: BaseElement; + private tooltipField: BaseElement; + private passwordField: BaseElement; + private inputFieldElement: BaseInputComponent; + + constructor(page: Page, selector: string) { + super(page, selector); + this.wrapper = new BaseElement(page, getTestIdSelector(getTestId(this.selector, InputTestIdModifiers.WRAPPER))); + this.inputField = new BaseElement(page, getTestIdSelector(getTestId(this.selector, InputTestIdModifiers.FIELD))); + this.tooltipField = new BaseElement(page, getTestIdSelector(getTestId(this.selector, InputTestIdModifiers.TOOLTIP))); + this.passwordField = new BaseElement(page, getTestIdSelector(getTestId(this.selector, InputTestIdModifiers.TOGGLE_PASSWORD))); + this.inputFieldElement = new BaseInputComponent(page, getTestIdSelector(getTestId(this.selector, InputTestIdModifiers.FIELD))); + } + + getInputsLabelText(): Promise { + const fieldLabelComponent = new FieldLabelComponent(this.page, this.selector); + return fieldLabelComponent.getLabelText(); + } + + isInputMandatory(): Promise { + const fieldLabelComponent = new FieldLabelComponent(this.page, this.selector); + return fieldLabelComponent.isMandatory(); + } + + hasInputExtraText(): Promise { + const fieldHelpTextComponent = new FieldHelpTextComponent(this.page, this.selector); + return fieldHelpTextComponent.hasExtraText(); + } + + getInputExtraText(): Promise { + const fieldHelpTextComponent = new FieldHelpTextComponent(this.page, this.selector); + return fieldHelpTextComponent.getExtraText(); + } + + async clickOnApplyButton(): Promise { + const applyButtonSelector = await this.wrapper.getLocator('.icon.icon-name--check'); + await applyButtonSelector.click(); + } + + async hasInlineErrorText(): Promise { + return (await this.tooltipField).isVisible(); + } + + async getInlineErrorText(): Promise { + const inlineErrorSelector = await this.tooltipField; + await inlineErrorSelector.hover(); + return inlineErrorSelector.getAttribute('text'); + } + + async isPasswordHidden(): Promise { + const hiddenPasswordLocator = await (await this.passwordField).getLocator('.fu-show-password-button eye-slash'); + return hiddenPasswordLocator.isVisible(); + } + + async clickOnPasswordIcon(): Promise { + const passwordFieldSelector = await this.passwordField; + await passwordFieldSelector.click(); + } + + async clickOnShowPassword(): Promise { + if (await this.isPasswordHidden()) { + await this.clickOnPasswordIcon(); + } + } + + async clickOnHidePassword(): Promise { + if (!(await this.isPasswordHidden())) { + await this.clickOnPasswordIcon(); + } + } + + async clearInput(): Promise { + await this.inputFieldElement.clearInput(); + } + + async isInputDisabled(): Promise { + return (await this.inputField).isDisabled(); + } + + async hasApplyButton(): Promise { + const applyButtonLocator: Locator = await (await this.wrapper).getLocator('.icon.icon-name--check'); + return applyButtonLocator.isVisible(); + } + + async getMaxLengthNumber(): Promise { + return (await this.inputField).getAttribute('maxlength'); + } + + async getActualNumberLength(): Promise { + const value: string = await (await this.inputField).getAttribute('value'); + return value.length; + } + + async getFontCaptionText(): Promise { + const fontCaptionLocator: Locator = await (await this.wrapper).getLocator('.font-caption'); + const fontCaptionText: string = await fontCaptionLocator.textContent(); + return fontCaptionText.split('/').map(str => parseInt(str.trim(), 10)); + } + + getHelpIconText(): Promise { + const fieldLabelComponent = new FieldLabelComponent(this.page, this.selector); + return fieldLabelComponent.getHelpIconText(); + } + + async hasExtraTextIconType({type}: HasHelpTextTypeParams): Promise { + const fieldHelpTextComponent = new FieldHelpTextComponent(this.page, this.selector); + return fieldHelpTextComponent.hasExtraTextIconType({testId: this.selector, type: type}); + } + + async getInputValue(): Promise { + return await this.getInputsFieldText(); + } + + async isValidationAppear(): Promise { + const fieldClasses: string = await (await this.wrapper).getAttribute('class'); + return fieldClasses.includes('variant-error'); + } +} diff --git a/projects/fusion-e2e/tests/components/inputs/types.ts b/projects/fusion-e2e/entities/components/inputs/types.ts similarity index 100% rename from projects/fusion-e2e/tests/components/inputs/types.ts rename to projects/fusion-e2e/entities/components/inputs/types.ts diff --git a/projects/fusion-e2e/tests/components/tabs/consts.ts b/projects/fusion-e2e/entities/components/tabs/consts.ts similarity index 97% rename from projects/fusion-e2e/tests/components/tabs/consts.ts rename to projects/fusion-e2e/entities/components/tabs/consts.ts index a715ec7ae..a67640226 100644 --- a/projects/fusion-e2e/tests/components/tabs/consts.ts +++ b/projects/fusion-e2e/entities/components/tabs/consts.ts @@ -1,4 +1,4 @@ -import {TabsTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {TabsTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../global/utils'; export const tabsStoryId = 'v4-components-tabs--basic'; diff --git a/projects/fusion-e2e/entities/components/tabs/tabs-component.ts b/projects/fusion-e2e/entities/components/tabs/tabs-component.ts new file mode 100644 index 000000000..8dee740f0 --- /dev/null +++ b/projects/fusion-e2e/entities/components/tabs/tabs-component.ts @@ -0,0 +1,46 @@ +import {TabsSelectionParams} from './types'; +import {TabsTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; +import {Locator, Page} from '@playwright/test'; +import {getTestId} from '../../global/utils'; +import {defaultTestId} from './consts'; + +export class TabsComponent extends BaseComponent { + private readonly disabledTestId: string; + + constructor(page: Page, selector: string) { + super(page, selector); + this.disabledTestId = getTestId(defaultTestId, TabsTestIdModifiers.TAB_DISABLED); + } + + // Get the text of the selected tab + async getSelectedTabText(): Promise { + const testIdSelector = getTestId(this.selector, TabsTestIdModifiers.WRAPPER); + const element: Locator = await this.getByTestId(testIdSelector); + const activeTab: Locator = element.locator('.tab-item--active'); + return activeTab.textContent(); + } + + // Select a tab + async selectTab({tabName}: TabsSelectionParams): Promise { + const tabIndex: number = await this.getTabIndex({tabName}); + const tabTestIdSelector = getTestId(this.selector, `${TabsTestIdModifiers.TAB}-${tabIndex + 1}`); + const tab: Locator = await this.getByTestId(tabTestIdSelector); + await tab.click(); + } + + // Get the index of a tab + private async getTabIndex({tabName}: TabsSelectionParams): Promise { + const testIdSelector = getTestId(this.selector, TabsTestIdModifiers.WRAPPER); + const wrapperLocator: Locator = await this.getByTestId(testIdSelector); + const tabs: string[] = await wrapperLocator.locator('.tab-item').allTextContents(); + return tabs.indexOf(tabName); + } + + // Check if a tab is disabled + async isTabDisabled(): Promise { + await this.waitForComponent(); + const disabledTab: Locator = await this.getByTestId(this.disabledTestId); + return disabledTab.isDisabled(); + } +} diff --git a/projects/fusion-e2e/entities/components/tabs/types.ts b/projects/fusion-e2e/entities/components/tabs/types.ts new file mode 100644 index 000000000..f28d9cdbd --- /dev/null +++ b/projects/fusion-e2e/entities/components/tabs/types.ts @@ -0,0 +1,4 @@ +export type TabsSelectionParams = { + testId?: string; + tabName: string; +}; diff --git a/projects/fusion-e2e/tests/components/tooltip/consts.ts b/projects/fusion-e2e/entities/components/tooltip/consts.ts similarity index 94% rename from projects/fusion-e2e/tests/components/tooltip/consts.ts rename to projects/fusion-e2e/entities/components/tooltip/consts.ts index 67c3ee639..faf3af0dc 100644 --- a/projects/fusion-e2e/tests/components/tooltip/consts.ts +++ b/projects/fusion-e2e/entities/components/tooltip/consts.ts @@ -1,4 +1,4 @@ -import {TooltipTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {TooltipTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; import {getTestId, getTestIdSelector} from '../../global/utils'; export const tooltipStoryId = 'v4-components-tooltip--basic'; diff --git a/projects/fusion-e2e/entities/components/tooltip/tooltip-component.ts b/projects/fusion-e2e/entities/components/tooltip/tooltip-component.ts new file mode 100644 index 000000000..30ab90110 --- /dev/null +++ b/projects/fusion-e2e/entities/components/tooltip/tooltip-component.ts @@ -0,0 +1,31 @@ +import {TooltipTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId} from '../../global/utils'; +import {BaseComponent} from '../base-component'; +import {Locator, Page} from '@playwright/test'; + +export class TooltipComponent extends BaseComponent { + private triggerLocator: Locator; + + constructor(page: Page, selector: string) { + super(page, selector); + this.initializeLocator().then(r => r); + } + + private async initializeLocator() { + const testIdSelector = getTestId(this.selector, TooltipTestIdModifiers.TRIGGER); + this.triggerLocator = await this.getByTestId(testIdSelector); + } + + // Get the text of the tooltip + async getTooltipText(): Promise { + const lastTriggerLocator: Locator = this.triggerLocator.last(); + const spanLocator: Locator = lastTriggerLocator.locator('span'); + return spanLocator.textContent(); + } + + // Get the header text of the tooltip + async getTooltipHeaderText(): Promise { + const lastTriggerLocator: Locator = this.triggerLocator.last(); + return lastTriggerLocator.getAttribute('header'); + } +} diff --git a/projects/fusion-e2e/tests/components/trendindicator/consts.ts b/projects/fusion-e2e/entities/components/trendindicator/consts.ts similarity index 100% rename from projects/fusion-e2e/tests/components/trendindicator/consts.ts rename to projects/fusion-e2e/entities/components/trendindicator/consts.ts diff --git a/projects/fusion-e2e/entities/components/trendindicator/trendindicator-component.ts b/projects/fusion-e2e/entities/components/trendindicator/trendindicator-component.ts new file mode 100644 index 000000000..c2e0bdf02 --- /dev/null +++ b/projects/fusion-e2e/entities/components/trendindicator/trendindicator-component.ts @@ -0,0 +1,23 @@ +import {getTestId} from '../../global/utils'; +import {TrendIndicatorTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; +import {Locator, Page} from '@playwright/test'; + +export class TrendIndicatorComponent extends BaseComponent { + locator: Locator; + + constructor(page: Page, selector: string) { + super(page, selector); + this.initializeLocator().then(r => r); + } + + private async initializeLocator() { + const testIdSelector = getTestId(this.selector, TrendIndicatorTestIdModifiers.VALUE); + this.locator = await this.getByTestId(testIdSelector); + } + + // Get the value of the trend indicator + async getTrendIndicatorValue(): Promise { + return this.locator.textContent(); + } +} diff --git a/projects/fusion-e2e/entities/constants/index.ts b/projects/fusion-e2e/entities/constants/index.ts new file mode 100644 index 000000000..09b7537dc --- /dev/null +++ b/projects/fusion-e2e/entities/constants/index.ts @@ -0,0 +1,8 @@ +export const PAGES_PATHS = { + OPTIMIZER_DASHBOARD: 'optimizer', + OPTIMIZER_DASHBOARD_CSV_MANAGER: 'optimizer-csv-manager', + CAMPAIGN_GENERATOR_NEW: 'generator/new', + MANAGEMENT: 'management' +}; + +export const GLOBAL_DEBOUNCE = 500; diff --git a/projects/fusion-e2e/entities/elements/constants.ts b/projects/fusion-e2e/entities/elements/constants.ts new file mode 100644 index 000000000..18343765b --- /dev/null +++ b/projects/fusion-e2e/entities/elements/constants.ts @@ -0,0 +1,28 @@ +export const SELECTORS = { + CHECKBOX: { + FUSION_CHECKBOX: 'fusion-checkbox', + INPUT: 'input>>nth=0', + LABEL: '.fu-label-text' + }, + FILE_INPUT: { + INPUT: 'input[type=file]' + }, + + TAG: { + REMOVE_BUTTON: '.fu-tag .tag-icon-close', + LABEL: '.fu-tag div >> nth=0' + }, + + STATIC_TEXT: { + LABEL: 'label' + }, + + INPUT: { + INPUT: 'input', + TEXT_AREA: 'textarea' + }, + + BUTTON: { + BUTTON: 'button' + } +}; diff --git a/projects/fusion-e2e/entities/elements/elements-class/button.ts b/projects/fusion-e2e/entities/elements/elements-class/button.ts new file mode 100644 index 000000000..3a67b803c --- /dev/null +++ b/projects/fusion-e2e/entities/elements/elements-class/button.ts @@ -0,0 +1,38 @@ +import {Page, test} from '@playwright/test'; +import {Clickable, BaseElement} from '../../behavior'; +import {StaticText} from '../index'; + +export class Button extends BaseElement { + protected button: Clickable; + protected label: StaticText; + + constructor(page: Page, selector: string) { + super(page, selector); + this.button = new Clickable(page, selector); + this.label = new StaticText(page, selector); + } + + async isIconExist(selector: string): Promise { + let numOfIcons: number; + + await test.step(`Is icon exist: ${selector}`, async () => { + const isButtonWithIcon = await this.getLocator(selector); + numOfIcons = await isButtonWithIcon.count(); + }); + + return numOfIcons > 0; + } + + async getButtonLabel(): Promise { + let labelSelector: string; + + await test.step(`Get button label`, async () => { + labelSelector = await this.label.getText(); + if (labelSelector === null) { + throw new Error(`Couldn't get text for the button: ${this.selector}`); + } + }); + + return labelSelector; + } +} diff --git a/projects/fusion-e2e/entities/elements/elements-class/checkbox.ts b/projects/fusion-e2e/entities/elements/elements-class/checkbox.ts new file mode 100644 index 000000000..89500fde3 --- /dev/null +++ b/projects/fusion-e2e/entities/elements/elements-class/checkbox.ts @@ -0,0 +1,31 @@ +import {Page} from '@playwright/test'; +import {Checkable} from '../../behavior'; +import {StaticText} from '../index'; +import {SELECTORS} from '../constants'; + +export class CheckBox extends Checkable { + protected input: Checkable; + protected label: StaticText; + + constructor(page: Page, selector: string) { + super(page, selector); + this.input = new Checkable(page, `${this.selector}, ${SELECTORS.CHECKBOX.INPUT}`); + this.label = new StaticText(page, `${this.selector} ${SELECTORS.CHECKBOX.LABEL}`); + } + + async getText(): Promise { + return await this.label.getText(); + } + + async check(): Promise { + await this.input.check(); + } + + async uncheck(): Promise { + await this.input.uncheck(); + } + + async isChecked(): Promise { + return await this.input.isChecked(); + } +} diff --git a/projects/fusion-e2e/entities/elements/elements-class/file-csv-upload-input.ts b/projects/fusion-e2e/entities/elements/elements-class/file-csv-upload-input.ts new file mode 100644 index 000000000..2d54ba8e4 --- /dev/null +++ b/projects/fusion-e2e/entities/elements/elements-class/file-csv-upload-input.ts @@ -0,0 +1,15 @@ +import {Page, test} from '@playwright/test'; +import {BaseElement} from '../../behavior'; +import {SELECTORS} from '../constants'; + +export class FileCSVUploadInput extends BaseElement { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async uploadFile(pathToFile: string) { + await test.step(`Upload file`, async () => { + await this.page.setInputFiles(`${this.selector} ${SELECTORS.FILE_INPUT.INPUT}`, pathToFile); + }); + } +} diff --git a/projects/fusion-e2e/entities/elements/elements-class/input.ts b/projects/fusion-e2e/entities/elements/elements-class/input.ts new file mode 100644 index 000000000..742b6015b --- /dev/null +++ b/projects/fusion-e2e/entities/elements/elements-class/input.ts @@ -0,0 +1,12 @@ +import {Page} from '@playwright/test'; +import {Editable} from '../../behavior'; + +export class Input extends Editable { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async getInputValue(): Promise { + return await this.locator.inputValue(); + } +} diff --git a/projects/fusion-e2e/entities/elements/elements-class/static-text.ts b/projects/fusion-e2e/entities/elements/elements-class/static-text.ts new file mode 100644 index 000000000..d87e0e415 --- /dev/null +++ b/projects/fusion-e2e/entities/elements/elements-class/static-text.ts @@ -0,0 +1,22 @@ +import {Page, test} from '@playwright/test'; +import {BaseElement} from '../../behavior'; + +export class StaticText extends BaseElement { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async getText(): Promise { + let text: string; + + await test.step(`Get text`, async () => { + const textLocator = await this.getLocator(this.selector); + text = await textLocator.textContent(); + if (!text) { + throw new Error(`Couldn't find text: ${text}`); + } + }); + + return text.trim(); + } +} diff --git a/projects/fusion-e2e/entities/elements/elements-class/tag.ts b/projects/fusion-e2e/entities/elements/elements-class/tag.ts new file mode 100644 index 000000000..a0ce304d9 --- /dev/null +++ b/projects/fusion-e2e/entities/elements/elements-class/tag.ts @@ -0,0 +1,24 @@ +import {Page} from '@playwright/test'; +import {BaseElement} from '../../behavior'; +import {StaticText} from '../index'; +import {Button} from '../index'; +import {SELECTORS} from '../constants'; + +export class Tag extends BaseElement { + readonly label: StaticText; + readonly removeButton: Button; + + constructor(page: Page, selector: string) { + super(page, selector); + this.label = new StaticText(page, SELECTORS.TAG.LABEL); + this.removeButton = new Button(page, SELECTORS.TAG.REMOVE_BUTTON); + } + + async getText(): Promise { + return await this.label.getText(); + } + + async remove(): Promise { + await this.removeButton.click(); + } +} diff --git a/projects/fusion-e2e/entities/elements/index.ts b/projects/fusion-e2e/entities/elements/index.ts new file mode 100644 index 000000000..88c2daa3d --- /dev/null +++ b/projects/fusion-e2e/entities/elements/index.ts @@ -0,0 +1,5 @@ +export {FileCSVUploadInput} from './elements-class/file-csv-upload-input'; +export {Input} from './elements-class/input'; +export {StaticText} from './elements-class/static-text'; +export {CheckBox} from './elements-class/checkbox'; +export {Button} from './elements-class/button'; diff --git a/projects/fusion-e2e/tests/global/types.ts b/projects/fusion-e2e/entities/global/types.ts similarity index 100% rename from projects/fusion-e2e/tests/global/types.ts rename to projects/fusion-e2e/entities/global/types.ts diff --git a/projects/fusion-e2e/tests/global/utils.ts b/projects/fusion-e2e/entities/global/utils.ts similarity index 89% rename from projects/fusion-e2e/tests/global/utils.ts rename to projects/fusion-e2e/entities/global/utils.ts index 089df6add..6ab822491 100644 --- a/projects/fusion-e2e/tests/global/utils.ts +++ b/projects/fusion-e2e/entities/global/utils.ts @@ -22,4 +22,4 @@ export const createStoryBookComponentPath = (componentId: string, componentParam export const getTestId = (testId: string, testIdModifier: string) => `${testId}--${testIdModifier}`; -export const getTestIdSelector = (testId: string) => `[data-testid='${testId}']`; +export const getTestIdSelector = (testId: string): string => `[data-testid='${testId}']`; diff --git a/projects/fusion-e2e/entities/index.ts b/projects/fusion-e2e/entities/index.ts new file mode 100644 index 000000000..da0efc7ce --- /dev/null +++ b/projects/fusion-e2e/entities/index.ts @@ -0,0 +1,29 @@ +export * from './components/alert/alert-component'; +export * from './components/base-component'; +export * from './global/types'; +export * from './components/dialog/dialog-component'; +export * from './components/dropdown/dropdown-v4/dropdown-component'; +export * from './components/dropdown/types'; +export * from './components/dropdown/base-dropdown'; +export * from './components/fieldLabel/field-label-component'; +export * from './components/inputs/input-v4/inputs-component'; +export * from './components/inputs/types'; +export * from './components/inputs/base-input'; +export * from './components/tabs/tabs-component'; +export * from './components/tooltip/tooltip-component'; +export * from './components/button/button-component'; +export * from './components/alert/alert-component'; +export * from './components/chart/chart-component'; +export * from './components/chip-filter/chipFilter-component'; +export * from './components/fieldHelpText/field-help-text-component'; +export * from './components/trendindicator/trendindicator-component'; +export * from './elements/elements-class/button'; +export * from './elements/elements-class/checkbox'; +export * from './elements/elements-class/file-csv-upload-input'; +export * from './elements/elements-class/input'; +export * from './elements/elements-class/static-text'; +export * from './elements/elements-class/tag'; +export * from './behavior/behavior-class/checkable'; +export * from './behavior/behavior-class/clickable'; +export * from './behavior/behavior-class/editable'; +export * from './behavior/behavior-class/base-element'; diff --git a/projects/fusion-e2e/entities/pages/alert-page.ts b/projects/fusion-e2e/entities/pages/alert-page.ts new file mode 100644 index 000000000..2267a034f --- /dev/null +++ b/projects/fusion-e2e/entities/pages/alert-page.ts @@ -0,0 +1,53 @@ +import {AlertComponent} from '../components/alert/alert-component'; +import {alertStoryId, defaultTestId, loadedPageSelector} from '../components/alert/const'; +import {Page} from '@playwright/test'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps} from './base-page/types'; + +export class AlertPage extends BasePage { + readonly alert: AlertComponent; + + constructor(page: Page) { + const alertProps: ComponentProps = { + page: page, + testId: defaultTestId, + componentId: alertStoryId, + loadedPageSelector: loadedPageSelector + }; + + super(alertProps); + this.alert = new AlertComponent(page, this.testId); + } + + getAlertText() { + return this.alert.getAlertText(); + } + + getAlertTitle() { + return this.alert.getAlertTitle(); + } + + async clickOnActionButton() { + await this.alert.clickOnActionButton(); + } + + getActionButtonText() { + return this.alert.getActionButtonText(); + } + + async closeAlert() { + await this.alert.closeAlert(); + } + + isAlertVisible() { + return this.alert.isAlertVisible(); + } + + async waitForComponent() { + await this.alert.waitForComponent(); + } + + getAlertIconType() { + return this.alert.getAlertIconType(); + } +} diff --git a/projects/fusion-e2e/tests/components/base-page/component-base-page.ts b/projects/fusion-e2e/entities/pages/base-page/base-page.ts similarity index 96% rename from projects/fusion-e2e/tests/components/base-page/component-base-page.ts rename to projects/fusion-e2e/entities/pages/base-page/base-page.ts index bbc10eb00..16f8f0424 100644 --- a/projects/fusion-e2e/tests/components/base-page/component-base-page.ts +++ b/projects/fusion-e2e/entities/pages/base-page/base-page.ts @@ -2,7 +2,7 @@ import {type Page} from '@playwright/test'; import {ComponentProps, GotoParams} from './types'; import {createStoryBookComponentPath, getTestIdSelector} from '../../global/utils'; -export class ComponentBasePage { +export class BasePage { readonly page: Page; readonly testId: string; readonly componentId: string; diff --git a/projects/fusion-e2e/tests/components/base-page/types.ts b/projects/fusion-e2e/entities/pages/base-page/types.ts similarity index 100% rename from projects/fusion-e2e/tests/components/base-page/types.ts rename to projects/fusion-e2e/entities/pages/base-page/types.ts diff --git a/projects/fusion-e2e/entities/pages/button-page.ts b/projects/fusion-e2e/entities/pages/button-page.ts new file mode 100644 index 000000000..15165d80f --- /dev/null +++ b/projects/fusion-e2e/entities/pages/button-page.ts @@ -0,0 +1,57 @@ +import {Page} from '@playwright/test'; +import {BasePage} from './base-page/base-page'; +import {ButtonComponent} from '../components/button/button-component'; +import {ComponentProps} from './base-page/types'; +import {buttonStoryId, defaultTestId, loadedPageSelector} from '../components/button/consts'; + +export class ButtonPage extends BasePage { + readonly button: ButtonComponent; + + constructor(page: Page) { + const buttonProps: ComponentProps = { + page: page, + testId: defaultTestId, + componentId: buttonStoryId, + loadedPageSelector: loadedPageSelector + }; + + super(buttonProps); + this.button = new ButtonComponent(page, this.testId); + } + + async waitForComponent() { + await this.button.waitForComponent(); + } + + async waitForToggleButtonComponent() { + await this.button.waitForToggleButtonComponent(); + } + + async clickOnButton() { + await this.button.clickOnButton(); + } + + async hoverOnButton() { + await this.button.hoverOnButton(); + } + + isButtonLoading() { + return this.button.isButtonLoading(); + } + + getButtonText() { + return this.button.getButtonText(); + } + + getIconButtonText() { + return this.button.getIconButtonText(); + } + + isButtonDisabled() { + return this.button.isButtonDisabled(); + } + + getToggleButtonFirstLabel() { + return this.button.getToggleButtonFirstLabel(); + } +} diff --git a/projects/fusion-e2e/entities/pages/chart-page.ts b/projects/fusion-e2e/entities/pages/chart-page.ts new file mode 100644 index 000000000..a1813e5e7 --- /dev/null +++ b/projects/fusion-e2e/entities/pages/chart-page.ts @@ -0,0 +1,61 @@ +import {Page} from '@playwright/test'; +import {chartStoryId, defaultTestId, labelTestId, loadedPageSelector} from '../components/chart/consts'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps, GotoParams} from './base-page/types'; +import {ChartComponent} from '../components/chart/chart-component'; +import {createStoryBookComponentPath} from '../global/utils'; + +export class ChartPage extends BasePage { + readonly chart: ChartComponent; + + constructor(page: Page) { + const chartProps: ComponentProps = { + page: page, + testId: defaultTestId, + componentId: chartStoryId, + loadedPageSelector: loadedPageSelector + }; + + super(chartProps); + this.chart = new ChartComponent(page, this.testId); + } + + async goto(gotoParams: GotoParams = {}) { + const componentParams = { + testId: defaultTestId, + ...gotoParams.additionalComponentParams + }; + + await this.page.goto(createStoryBookComponentPath(gotoParams.storyId || this.componentId, componentParams)); + + await this.page.waitForSelector(this.loadedPageSelector); + } + + async waitForComponent() { + await this.chart.waitForComponent(); + } + + async waitForIconLabelComponent() { + await this.chart.waitForSelector('fusion-chart-labels'); + } + + async getLabelText(idx) { + return this.chart.getLabelText({testId: `${labelTestId}_${idx}`}); + } + + async getLabelIconText(idx) { + return this.chart.getLabelText({testId: `${defaultTestId}_${idx}`}); + } + + async getLabels() { + return this.chart.getLabels(); + } + + async getLabelColor(label) { + return this.chart.getColor(label); + } + + async getLabelIcon(label) { + return this.chart.getIcon(label); + } +} diff --git a/projects/fusion-e2e/tests/components/chip-filter/chipFilter-page.ts b/projects/fusion-e2e/entities/pages/chipFilter-page.ts similarity index 56% rename from projects/fusion-e2e/tests/components/chip-filter/chipFilter-page.ts rename to projects/fusion-e2e/entities/pages/chipFilter-page.ts index 8dfcd904a..2d6644182 100644 --- a/projects/fusion-e2e/tests/components/chip-filter/chipFilter-page.ts +++ b/projects/fusion-e2e/entities/pages/chipFilter-page.ts @@ -1,12 +1,12 @@ import {Page} from '@playwright/test'; -import {chipFilterStoryId, defaultTestId, loadedPageSelector} from './consts'; -import {ComponentBasePage} from '../base-page/component-base-page'; -import {ComponentProps, GotoParams} from '../base-page/types'; -import {ChipFilterComponent} from './chipFilter-component'; -import {createStoryBookComponentPath} from '../../global/utils'; +import {chipFilterStoryId, defaultTestId, loadedPageSelector} from '../components/chip-filter/consts'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps, GotoParams} from './base-page/types'; +import {ChipFilterComponent} from '../components/chip-filter/chipFilter-component'; +import {createStoryBookComponentPath} from '../global/utils'; -export class ChipFilterPage extends ComponentBasePage { - readonly component: ChipFilterComponent; +export class ChipFilterPage extends BasePage { + readonly chipFilter: ChipFilterComponent; constructor(page: Page) { const chipFilterProps: ComponentProps = { @@ -17,11 +17,11 @@ export class ChipFilterPage extends ComponentBasePage { }; super(chipFilterProps); - this.component = new ChipFilterComponent(page); + this.chipFilter = new ChipFilterComponent(page, this.testId); } async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); + await this.chipFilter.waitForComponent(); } async goto(gotoParams: GotoParams = {}) { @@ -35,11 +35,11 @@ export class ChipFilterPage extends ComponentBasePage { await this.page.waitForSelector(this.loadedPageSelector); } - click() { - return this.component.click({testId: this.testId}); + clickChipFilter() { + return this.chipFilter.clickChipFilter(); } getChipFilterLabel() { - return this.component.getChipFilterLabel({testId: this.testId}); + return this.chipFilter.getChipFilterLabel(); } } diff --git a/projects/fusion-e2e/tests/components/dialog/dialog-page.ts b/projects/fusion-e2e/entities/pages/dialog-page.ts similarity index 50% rename from projects/fusion-e2e/tests/components/dialog/dialog-page.ts rename to projects/fusion-e2e/entities/pages/dialog-page.ts index bf2391a7c..0fa56d43b 100644 --- a/projects/fusion-e2e/tests/components/dialog/dialog-page.ts +++ b/projects/fusion-e2e/entities/pages/dialog-page.ts @@ -1,12 +1,13 @@ import {Page} from '@playwright/test'; -import {DialogComponent} from './dialog-component'; -import {dialogStoryId, defaultTestId, loadedPageSelector} from './consts'; -import {ComponentProps, GotoParams} from '../base-page/types'; -import {createStoryBookComponentPath} from '../../global/utils'; -import {ComponentBasePage} from '../base-page/component-base-page'; +import {DialogComponent} from '../components/dialog/dialog-component'; +import {dialogStoryId, defaultTestId, loadedPageSelector} from '../components/dialog/consts'; +import {ComponentProps, GotoParams} from './base-page/types'; +import {createStoryBookComponentPath} from '../global/utils'; +import {BasePage} from './base-page/base-page'; +import {DialogTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; -export class DialogPage extends ComponentBasePage { - readonly component: DialogComponent; +export class DialogPage extends BasePage { + readonly dialog: DialogComponent; constructor(page: Page) { const dialogProps: ComponentProps = { @@ -17,7 +18,7 @@ export class DialogPage extends ComponentBasePage { }; super(dialogProps); - this.component = new DialogComponent(page); + this.dialog = new DialogComponent(page, this.testId); } async goto(gotoParams: GotoParams = {}) { @@ -32,42 +33,44 @@ export class DialogPage extends ComponentBasePage { } getDialogTitle() { - return this.component.getDialogTitle({testId: this.testId}); + return this.dialog.getDialogTitle(); } openDialog() { - return this.component.openDialog({testId: this.testId}); + return this.dialog.openDialog(); } getDialogText() { - return this.component.getDialogText({testId: this.testId}); + return this.dialog.getDialogText(); } getDialogSubtitle() { - return this.component.getDialogSubtitle({testId: this.testId}); + return this.dialog.getDialogSubtitle(); } async closeDialog() { - await this.component.closeDialog({testId: this.testId}); + await this.dialog.closeDialog(); } async clickOnPrimaryButton() { - await this.component.clickOnPrimaryButton({testId: this.testId}); + await this.dialog.clickOnPrimaryButton(); } async clickOnDefaultButton() { - await this.component.clickOnDefaultButton({testId: this.testId}); + await this.dialog.clickOnDefaultButton(); } async clickOnDeleteButton() { - await this.component.clickOnDeleteButton({testId: this.testId}); + await this.dialog.clickOnDeleteButton(); } async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); + // const loadedPageSelector = getTestIdSelector(getTestId(testId, DialogTestIdModifiers.WRAPPER)); + + await this.dialog.waitForComponent(DialogTestIdModifiers.WRAPPER); } isDialogVisible() { - return this.component.isDialogVisible({testId: this.testId}); + return this.dialog.isDialogVisible(); } } diff --git a/projects/fusion-e2e/entities/pages/dropdown-page.ts b/projects/fusion-e2e/entities/pages/dropdown-page.ts new file mode 100644 index 000000000..155eef648 --- /dev/null +++ b/projects/fusion-e2e/entities/pages/dropdown-page.ts @@ -0,0 +1,115 @@ +import {Page} from '@playwright/test'; +import {dropdownDefaultStoryId, loadedPageSelector} from '../components/dropdown/dropdown-v4/consts'; +import {DropdownComponent} from '../components/dropdown/dropdown-v4/dropdown-component'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps} from './base-page/types'; +import {testIdWithIndex} from '../components/dropdown/consts'; + +export class DropdownPage extends BasePage { + readonly dropdown: DropdownComponent; + + constructor(page: Page) { + const dropdownProps: ComponentProps = { + page: page, + testId: testIdWithIndex, + componentId: dropdownDefaultStoryId, + loadedPageSelector: loadedPageSelector + }; + + super(dropdownProps); + this.dropdown = new DropdownComponent(page, this.testId); + } + + async selectDropdownOptionByIndex(index: number) { + await this.dropdown.selectDropdownOptionByIndex({ + testId: this.testId, + index + }); + } + + getSelectedLabel() { + return this.dropdown.getSelectedLabel(); + } + + getDropdownTitle() { + return this.dropdown.getDropdownTitle(); + } + + async selectDropdownOptionByName(name: string) { + await this.dropdown.selectDropdownOptionByName({ + testId: this.testId, + name + }); + } + + async selectMultipleItems(itemsToSelect: number[], applyChanges = true) { + await this.dropdown.selectMultipleItemsByIndex({ + testId: this.testId, + itemsToSelect + }); + if (applyChanges) { + await this.dropdown.clickOnApply(); + } + } + + async clickOnCancel() { + await this.dropdown.clickOnCancel(); + } + + searchForItem(searchTerm: string) { + return this.dropdown.searchForItem({ + selector: this.testId, + searchTerm + }); + } + + async selectMultipleItemsByName(itemsToSelect: string[], applyChanges = true) { + await this.dropdown.selectMultipleItemsByName({ + testId: this.testId, + itemsToSelect + }); + if (applyChanges) { + await this.dropdown.clickOnApply(); + } + } + + isErrorState() { + return this.dropdown.isErrorText(); + } + + isDisabled() { + return this.dropdown.isDropdownDisabled(); + } + + async clearAllOptions() { + await this.dropdown.clearAllOptions(); + } + + async openDropdownComponent() { + await this.dropdown.openDropdownComponent(); + } + + async clickOnApply() { + await this.dropdown.clickOnApply(); + } + + isSelectAllChecked() { + return this.dropdown.isSelectAllChecked(); + } + + isSelectAllIndeterminate() { + return this.dropdown.isSelectAllIndeterminate(); + } + + async removeChipSelection() { + await this.dropdown.removeChipSelection(); + } + + async waitForComponent() { + await this.dropdown.waitForComponent(); + } + + getDropdownOptions() { + return this.dropdown.getDropdownOptions(); + } +} diff --git a/projects/fusion-e2e/entities/pages/inputs-page.ts b/projects/fusion-e2e/entities/pages/inputs-page.ts new file mode 100644 index 000000000..70815d4f3 --- /dev/null +++ b/projects/fusion-e2e/entities/pages/inputs-page.ts @@ -0,0 +1,115 @@ +import {Page} from '@playwright/test'; +import {InputsComponent} from '../components/inputs/input-v4/inputs-component'; +import {defaultTestId, inputsStoryId, loadedPageSelector} from '../components/inputs/input-v4/consts'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps} from './base-page/types'; +import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; + +export class InputsPage extends BasePage { + readonly input: InputsComponent; + + constructor(page: Page) { + const inputsProps: ComponentProps = { + page: page, + testId: defaultTestId, + componentId: inputsStoryId, + loadedPageSelector: loadedPageSelector + }; + + super(inputsProps); + this.input = new InputsComponent(page, this.testId); + } + + getInputsFieldText() { + return this.input.getInputsFieldText(); + } + + getInputsLabelText() { + return this.input.getInputsLabelText(); + } + + addInput({textInput}: {textInput: string}) { + return this.input.addInput({ + text: textInput + }); + } + + isInputMandatory() { + return this.input.isInputMandatory(); + } + + getPlaceholderText() { + return this.input.getPlaceholderText(); + } + + hasInputExtraText() { + return this.input.hasInputExtraText(); + } + + getInputExtraText() { + return this.input.getInputExtraText(); + } + + getInputsType() { + return this.input.getInputsType(); + } + + hasInlineErrorText() { + return this.input.hasInlineErrorText(); + } + + getInlineErrorText() { + return this.input.getInlineErrorText(); + } + + async clickOnApplyButton() { + await this.input.clickOnApplyButton(); + } + + hasApplyButton() { + return this.input.hasApplyButton(); + } + + hasExtraTextIconType({type}: {type: string}) { + return this.input.hasExtraTextIconType({ + testId: this.testId, + type: type + }); + } + + isInputDisabled() { + return this.input.isInputDisabled(); + } + + getMaxLengthNumber() { + return this.input.getMaxLengthNumber(); + } + + getActualNumberLength() { + return this.input.getActualNumberLength(); + } + + getHelpIconText() { + return this.input.getHelpIconText(); + } + + async clickOnShowPassword() { + await this.input.clickOnShowPassword(); + } + + async clickOnHidePassword() { + await this.input.clickOnHidePassword(); + } + + isPasswordHidden() { + return this.input.isPasswordHidden(); + } + + async waitForComponent() { + return this.input.waitForComponent(InputTestIdModifiers.WRAPPER); + } + + async clearInput() { + await this.input.clearInput(); + } +} diff --git a/projects/fusion-e2e/entities/pages/tabs-page.ts b/projects/fusion-e2e/entities/pages/tabs-page.ts new file mode 100644 index 000000000..36395c920 --- /dev/null +++ b/projects/fusion-e2e/entities/pages/tabs-page.ts @@ -0,0 +1,37 @@ +import {Page} from '@playwright/test'; +import {tabsStoryId, loadedPageSelector, wrapperTestId} from '../components/tabs/consts'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps} from './base-page/types'; +import {TabsComponent} from '../components/tabs/tabs-component'; + +export class TabsPage extends BasePage { + readonly tabs: TabsComponent; + + constructor(page: Page) { + const tabsProps: ComponentProps = { + page: page, + testId: wrapperTestId, + componentId: tabsStoryId, + loadedPageSelector: loadedPageSelector + }; + + super(tabsProps); + this.tabs = new TabsComponent(page, this.testId); + } + + async waitForComponent() { + await this.tabs.waitForComponent(); + } + + getSelectedTabText() { + return this.tabs.getSelectedTabText(); + } + + async selectTab({tabName}: {tabName: string}) { + await this.tabs.selectTab({tabName}); + } + + isTabDisabled() { + return this.tabs.isTabDisabled(); + } +} diff --git a/projects/fusion-e2e/tests/components/tooltip/tooltip-page.ts b/projects/fusion-e2e/entities/pages/tooltip-page.ts similarity index 64% rename from projects/fusion-e2e/tests/components/tooltip/tooltip-page.ts rename to projects/fusion-e2e/entities/pages/tooltip-page.ts index 89f25ac40..b1c92ab8e 100644 --- a/projects/fusion-e2e/tests/components/tooltip/tooltip-page.ts +++ b/projects/fusion-e2e/entities/pages/tooltip-page.ts @@ -1,13 +1,13 @@ import {Page} from '@playwright/test'; -import {tooltipStoryId, defaultTestId, loadedPageSelector} from './consts'; -import {ComponentBasePage} from '../base-page/component-base-page'; -import {ComponentProps, GotoParams} from '../base-page/types'; -import {TooltipComponent} from './tooltip-component'; -import {createStoryBookComponentPath, getTestId} from '../../global/utils'; -import {TooltipTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {tooltipStoryId, defaultTestId, loadedPageSelector} from '../components/tooltip/consts'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps, GotoParams} from './base-page/types'; +import {TooltipComponent} from '../components/tooltip/tooltip-component'; +import {createStoryBookComponentPath, getTestId} from '../global/utils'; +import {TooltipTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; -export class TooltipPage extends ComponentBasePage { - readonly component: TooltipComponent; +export class TooltipPage extends BasePage { + readonly tooltip: TooltipComponent; constructor(page: Page) { const tooltipProps: ComponentProps = { @@ -18,11 +18,11 @@ export class TooltipPage extends ComponentBasePage { }; super(tooltipProps); - this.component = new TooltipComponent(page); + this.tooltip = new TooltipComponent(page, this.testId); } async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); + await this.tooltip.waitForComponent(TooltipTestIdModifiers.TRIGGER); } async goto(gotoParams: GotoParams = {}) { @@ -38,11 +38,11 @@ export class TooltipPage extends ComponentBasePage { } getTooltipText() { - return this.component.getTooltipText({testId: this.testId}); + return this.tooltip.getTooltipText(); } getTooltipHeaderText() { - return this.component.getTooltipHeaderText({testId: this.testId}); + return this.tooltip.getTooltipHeaderText(); } private async openTooltip() { diff --git a/projects/fusion-e2e/entities/pages/trend-indicator-page.ts b/projects/fusion-e2e/entities/pages/trend-indicator-page.ts new file mode 100644 index 000000000..324c3649f --- /dev/null +++ b/projects/fusion-e2e/entities/pages/trend-indicator-page.ts @@ -0,0 +1,29 @@ +import {TrendIndicatorComponent} from '../components/trendindicator/trendindicator-component'; +import {trendIndicatorStoryId, defaultTestId, loadedPageSelector} from '../components/trendindicator/consts'; +import {Page} from '@playwright/test'; +import {BasePage} from './base-page/base-page'; +import {ComponentProps} from './base-page/types'; + +export class TrendIndicatorPage extends BasePage { + readonly trendIndicator: TrendIndicatorComponent; + + constructor(page: Page) { + const alertProps: ComponentProps = { + page: page, + testId: defaultTestId, + componentId: trendIndicatorStoryId, + loadedPageSelector: loadedPageSelector + }; + + super(alertProps); + this.trendIndicator = new TrendIndicatorComponent(page, this.testId); + } + + async waitForComponent() { + await this.trendIndicator.waitForComponent(); + } + + async getTrendIndicatorValue() { + return this.trendIndicator.getTrendIndicatorValue(); + } +} diff --git a/projects/fusion-e2e/tests/components/alert/alert.pw.ts b/projects/fusion-e2e/entities/tests/alert/alert.pw.ts similarity index 94% rename from projects/fusion-e2e/tests/components/alert/alert.pw.ts rename to projects/fusion-e2e/entities/tests/alert/alert.pw.ts index cd64ed4af..e1b715a3b 100644 --- a/projects/fusion-e2e/tests/components/alert/alert.pw.ts +++ b/projects/fusion-e2e/entities/tests/alert/alert.pw.ts @@ -1,6 +1,6 @@ -import {AlertPage} from './alert-page'; +import {AlertPage} from '../../pages/alert-page'; import {expect, test} from '@playwright/test'; -import {alertSeveritiesStoryId} from './consts'; +import {alertSeveritiesStoryId} from './const'; let component: AlertPage; diff --git a/projects/fusion-e2e/entities/tests/alert/const.ts b/projects/fusion-e2e/entities/tests/alert/const.ts new file mode 100644 index 000000000..ad19ed130 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/alert/const.ts @@ -0,0 +1,7 @@ +import {AlertTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export const alertStoryId = 'v4-components-feedback-alert--basic'; +export const alertSeveritiesStoryId = 'v4-components-feedback-alert--severities'; +export const defaultTestId = 'alert-default'; +export const loadedPageSelector = getTestIdSelector(getTestId(defaultTestId, AlertTestIdModifiers.WRAPPER)); diff --git a/projects/fusion-e2e/entities/tests/base-component.ts b/projects/fusion-e2e/entities/tests/base-component.ts new file mode 100644 index 000000000..7e1bdda20 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/base-component.ts @@ -0,0 +1,20 @@ +import {Page} from '@playwright/test'; +import {BaseElement} from '../behavior'; + +export class BaseComponent extends BaseElement { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async isAllControlsVisible(controls: BaseElement[]) { + let isVisible = true; + for (const control of controls) { + const isControlVisible = await control.isVisible(); + if (!isControlVisible) { + isVisible = false; + break; + } + } + return isVisible; + } +} diff --git a/projects/fusion-e2e/tests/components/button/button.pw.ts b/projects/fusion-e2e/entities/tests/button/button.pw.ts similarity index 95% rename from projects/fusion-e2e/tests/components/button/button.pw.ts rename to projects/fusion-e2e/entities/tests/button/button.pw.ts index a6824677e..b7c7c7617 100644 --- a/projects/fusion-e2e/tests/components/button/button.pw.ts +++ b/projects/fusion-e2e/entities/tests/button/button.pw.ts @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/test'; -import {ButtonPage} from './button-page'; +import {ButtonPage} from '../../pages/button-page'; let buttonPage: ButtonPage; diff --git a/projects/fusion-e2e/entities/tests/button/consts.ts b/projects/fusion-e2e/entities/tests/button/consts.ts new file mode 100644 index 000000000..ac88a26f4 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/button/consts.ts @@ -0,0 +1,9 @@ +import {getTestIdSelector} from '../../global/utils'; + +export const buttonStoryId = 'v4-components-buttons-button--basic'; +export const iconButtonStoryId = 'v4-components-buttons-iconbutton--basic'; + +export const toggleButtonStoryId = 'v4-components-buttons-togglebutton--default'; +export const defaultTestId = 'buttonTestId'; +export const iconButtonTestId = 'iconButtonTestId'; +export const loadedPageSelector = getTestIdSelector(defaultTestId); diff --git a/projects/fusion-e2e/tests/components/button/iconButton.pw.ts b/projects/fusion-e2e/entities/tests/button/iconButton.pw.ts similarity index 90% rename from projects/fusion-e2e/tests/components/button/iconButton.pw.ts rename to projects/fusion-e2e/entities/tests/button/iconButton.pw.ts index 779e54e1f..d299f83aa 100644 --- a/projects/fusion-e2e/tests/components/button/iconButton.pw.ts +++ b/projects/fusion-e2e/entities/tests/button/iconButton.pw.ts @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/test'; -import {ButtonPage} from './button-page'; +import {ButtonPage} from '../../pages/button-page'; import {iconButtonStoryId} from './consts'; let buttonPage: ButtonPage; diff --git a/projects/fusion-e2e/tests/components/button/toggleButton.pw.ts b/projects/fusion-e2e/entities/tests/button/toggleButton.pw.ts similarity index 90% rename from projects/fusion-e2e/tests/components/button/toggleButton.pw.ts rename to projects/fusion-e2e/entities/tests/button/toggleButton.pw.ts index fff8aa8bc..af8021a6e 100644 --- a/projects/fusion-e2e/tests/components/button/toggleButton.pw.ts +++ b/projects/fusion-e2e/entities/tests/button/toggleButton.pw.ts @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/test'; -import {ButtonPage} from './button-page'; +import {ButtonPage} from '../../pages/button-page'; import {toggleButtonStoryId} from './consts'; let buttonPage: ButtonPage; diff --git a/projects/fusion-e2e/tests/components/chart/chart.pw.ts b/projects/fusion-e2e/entities/tests/chart/chart.pw.ts similarity index 54% rename from projects/fusion-e2e/tests/components/chart/chart.pw.ts rename to projects/fusion-e2e/entities/tests/chart/chart.pw.ts index bb9f8572b..69b43b302 100644 --- a/projects/fusion-e2e/tests/components/chart/chart.pw.ts +++ b/projects/fusion-e2e/entities/tests/chart/chart.pw.ts @@ -1,5 +1,6 @@ import {expect, test} from '@playwright/test'; -import {ChartPage} from './chart-page'; +import {ChartPage} from '../../pages/chart-page'; +import {CHART_BAR_GROUPED_DATA_MOCK} from '@ironsource/fusion-ui/components/chart/v4/stories/chart-v4.component.mock'; let chartPage: ChartPage; @@ -16,6 +17,11 @@ test('Validate component is loaded', async () => { test('Check chart label text appears', async () => { await chartPage.goto(); await chartPage.waitForComponent(); - const labelText = await chartPage.getLabelText(); - expect(labelText).toContain('Unity Ads'); + + const dataKeys = Object.keys(CHART_BAR_GROUPED_DATA_MOCK.data); + + for (let i = 0; i < dataKeys.length; i++) { + const labelText = await chartPage.getLabelText(i); + expect(labelText).toContain(dataKeys[i]); + } }); diff --git a/projects/fusion-e2e/entities/tests/chart/chartLabelIcon.pw.ts b/projects/fusion-e2e/entities/tests/chart/chartLabelIcon.pw.ts new file mode 100644 index 000000000..2f74de311 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/chart/chartLabelIcon.pw.ts @@ -0,0 +1,34 @@ +import {expect, Locator, test} from '@playwright/test'; +import {ChartPage} from '../../pages/chart-page'; +import {chartLabelWithIconStoryId} from './consts'; +import {CART_LABELS_ICONS_MOCK} from '@ironsource/fusion-ui/components/chart-labels/v4/chart-labels-v4.stories.mock'; +import {checkLabelProperties} from '../utils/chartUtils'; + +let chartPage: ChartPage; + +test.beforeEach(async ({page}) => { + chartPage = new ChartPage(page); + await chartPage.goto({storyId: chartLabelWithIconStoryId}); + await chartPage.waitForComponent(); +}); + +test('Reloads the icon label component', async () => { + await chartPage.waitForIconLabelComponent(); +}); + +test('Checks the number and properties of labels', async () => { + const labels: Locator = await chartPage.getLabels(); + const labelsCount = await labels.count(); + expect(labelsCount).toBe(CART_LABELS_ICONS_MOCK.length); + + for (let i = 0; i < labelsCount; i++) { + await checkLabelProperties(i, labels, chartPage); + } +}); + +test('Checks that chart label text appears', async () => { + const firstLabelText = await chartPage.getLabelIconText(0); + const secondLabelText = await chartPage.getLabelIconText(1); + expect(firstLabelText).toContain(CART_LABELS_ICONS_MOCK[0].label); + expect(secondLabelText).toContain(CART_LABELS_ICONS_MOCK[1].label); +}); diff --git a/projects/fusion-e2e/entities/tests/chart/consts.ts b/projects/fusion-e2e/entities/tests/chart/consts.ts new file mode 100644 index 000000000..4e8111bab --- /dev/null +++ b/projects/fusion-e2e/entities/tests/chart/consts.ts @@ -0,0 +1,10 @@ +import {ChartLabelTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export const chartStoryId = 'v4-components-datavisualization-charts-barchart--stack'; +export const chartLabelWithIconStoryId = 'v4-components-datavisualization-legenditems--with-icon'; +export const defaultTestId = 'charts-default'; + +export const labelTestId = getTestId(defaultTestId, ChartLabelTestIdModifiers.LABEL); + +export const loadedPageSelector = getTestIdSelector(labelTestId); diff --git a/projects/fusion-e2e/tests/components/chip-filter/chipFilter.pw.ts b/projects/fusion-e2e/entities/tests/chip-filter/chipFilter.pw.ts similarity index 88% rename from projects/fusion-e2e/tests/components/chip-filter/chipFilter.pw.ts rename to projects/fusion-e2e/entities/tests/chip-filter/chipFilter.pw.ts index c21e9856d..f74d37a72 100644 --- a/projects/fusion-e2e/tests/components/chip-filter/chipFilter.pw.ts +++ b/projects/fusion-e2e/entities/tests/chip-filter/chipFilter.pw.ts @@ -1,4 +1,4 @@ -import {ChipFilterPage} from './chipFilter-page'; +import {ChipFilterPage} from '../../pages/chipFilter-page'; import {expect, test} from '@playwright/test'; let chipFilterPage: ChipFilterPage; @@ -22,6 +22,6 @@ test('should work when clicked', async () => { test('should show option dropdown when clicked', async ({page}) => { await chipFilterPage.goto(); await chipFilterPage.waitForComponent(); - await chipFilterPage.click(); + await chipFilterPage.clickChipFilter(); await expect(await page.locator('.options-dropdown').first()).toBeVisible(); }); diff --git a/projects/fusion-e2e/entities/tests/chip-filter/consts.ts b/projects/fusion-e2e/entities/tests/chip-filter/consts.ts new file mode 100644 index 000000000..16ffb2dc7 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/chip-filter/consts.ts @@ -0,0 +1,17 @@ +import {ChipFilterTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export const chipFilterStoryId = 'v4-components-filterpanel--default'; +export const defaultTestId = 'filterpanel-default'; + +export const triggerTestId = getTestId(defaultTestId, ChipFilterTestIdModifiers.CHIP_FILTER); + +export const loadedPageSelector = getTestIdSelector(getTestId(defaultTestId, ChipFilterTestIdModifiers.CHIP_FILTER)); + +export const SELECTORS = { + CHIP_TEXT: '.fu-chip-text', + CHIP_LEFT_ICON: '.fu-chip-left-icon', + CHIP_RIGHT_ICON: '.fu-chip-right-icon', + ICON_CLOSE: '.fu-icon-close', + CHIP_CONTENT: '.fu-chip-content' +}; diff --git a/projects/fusion-e2e/entities/tests/constants.ts b/projects/fusion-e2e/entities/tests/constants.ts new file mode 100644 index 000000000..18343765b --- /dev/null +++ b/projects/fusion-e2e/entities/tests/constants.ts @@ -0,0 +1,28 @@ +export const SELECTORS = { + CHECKBOX: { + FUSION_CHECKBOX: 'fusion-checkbox', + INPUT: 'input>>nth=0', + LABEL: '.fu-label-text' + }, + FILE_INPUT: { + INPUT: 'input[type=file]' + }, + + TAG: { + REMOVE_BUTTON: '.fu-tag .tag-icon-close', + LABEL: '.fu-tag div >> nth=0' + }, + + STATIC_TEXT: { + LABEL: 'label' + }, + + INPUT: { + INPUT: 'input', + TEXT_AREA: 'textarea' + }, + + BUTTON: { + BUTTON: 'button' + } +}; diff --git a/projects/fusion-e2e/entities/tests/dialog/consts.ts b/projects/fusion-e2e/entities/tests/dialog/consts.ts new file mode 100644 index 000000000..404e784ae --- /dev/null +++ b/projects/fusion-e2e/entities/tests/dialog/consts.ts @@ -0,0 +1,19 @@ +import {DialogTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export const dialogStoryId = 'v4-components-feedback-dialog-dialog--basic'; +export const dialogDeleteStoryId = 'v4-components-dialog--delete'; +export const defaultTestId = 'dialog-default'; + +export const wrapperTestId = getTestId(defaultTestId, DialogTestIdModifiers.WRAPPER); + +export const loadedPageSelector = getTestIdSelector(wrapperTestId); + +export const SELECTORS = { + MODAL: '.modal', + SUBMIT_BUTTON: '.fu-primary-button', + CANCEL_BUTTON: 'fusion-modal-footer .fu-secondary-button', + MODAL_CONTENT: 'fusion-modal-content', + HEADER_TITLE: 'fusion-modal-header .modal-header-title', + CLOSE_BUTTON: 'fusion-modal-header .fu-close' +}; diff --git a/projects/fusion-e2e/tests/components/dialog/dialog.pw.ts b/projects/fusion-e2e/entities/tests/dialog/dialog.pw.ts similarity index 97% rename from projects/fusion-e2e/tests/components/dialog/dialog.pw.ts rename to projects/fusion-e2e/entities/tests/dialog/dialog.pw.ts index 4e41b9d3c..3b8cd9881 100644 --- a/projects/fusion-e2e/tests/components/dialog/dialog.pw.ts +++ b/projects/fusion-e2e/entities/tests/dialog/dialog.pw.ts @@ -1,5 +1,5 @@ import {dialogDeleteStoryId} from './consts'; -import {DialogPage} from './dialog-page'; +import {DialogPage} from '../../pages/dialog-page'; import {expect, test} from '@playwright/test'; let dialogPage: DialogPage; diff --git a/projects/fusion-e2e/entities/tests/dropdown/base-dropdown.ts b/projects/fusion-e2e/entities/tests/dropdown/base-dropdown.ts new file mode 100644 index 000000000..9543cf548 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/dropdown/base-dropdown.ts @@ -0,0 +1,209 @@ +import {getTestId} from '../../global/utils'; +import {SelectionByIndex, SelectionByName, SelectMultiple, SelectMultipleByName} from './types'; +import {DropdownTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; +import {expect, Page, test} from '@playwright/test'; +import {SELECTORS} from './consts'; +import {GLOBAL_DEBOUNCE} from '../../constants'; +import {StaticText} from '../static-text'; +import {BaseInputComponent} from '../inputs/base-input'; +import {ButtonComponent} from '../../components/button/button-component'; + +export class BaseDropdownComponent extends BaseComponent { + searchInput: BaseInputComponent; + option: ButtonComponent; + selectedOptionLabel: StaticText; + + constructor(page: Page, selector: string) { + super(page, selector); + this.searchInput = new BaseInputComponent(page, `${selector} ${SELECTORS.SEARCH_INPUT}`); + this.option = new ButtonComponent(page, `${selector} ${SELECTORS.FUSION_DROPDOWN_OPTIONS}`); + this.selectedOptionLabel = new StaticText(page, `${selector} ${SELECTORS.SELECTED_OPTION}`); + } + + async waitForComponent() { + await this.getByTestId(getTestId(this.selector, DropdownTestIdModifiers.WRAPPER)); + } + + async selectDropdownOptionByIndex({testId, index}: SelectionByIndex) { + await this.openDropdownComponent({testId: testId}); + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator = element.locator('fusion-dropdown-options-list > li').nth(index); + await locator.click(); + } + + async getDropdownButtonContent(testId: string) { + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.BUTTON_CONTENT)); + return element.textContent(); + } + + async openDropdownComponent({testId}: {testId: string}) { + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.TRIGGER)); + await element.click({ + position: { + x: 15, + y: 15 + } + }); + } + + async selectDropdownOptionByName({testId, name, shouldOpen = true}: SelectionByName) { + if (shouldOpen) await this.openDropdownComponent({testId: testId}); + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator = element.locator('fusion-dropdown-options-list > li', {hasText: name}); + await locator.click(); + } + + async selectMultipleItemsByIndex({testId, itemsToSelect}: SelectMultiple) { + await this.openDropdownComponent({testId: testId}); + for (const i of itemsToSelect) { + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator = element.locator('ul > li').nth(i); + await locator.click(); + } + } + + async selectMultipleItemsByName({testId, itemsToSelect}: SelectMultipleByName) { + await this.openDropdownComponent({testId: testId}); + for (const name of itemsToSelect) { + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)); + const locator = element.locator('ul > li', {hasText: name}).first(); + await locator.click(); + } + } + + async clickOnApply({testId}: {testId: string}) { + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.ACTION_APPLY)); + await element.click(); + } + + async clickOnCancel({testId}: {testId: string}) { + const element = await this.getByTestId(getTestId(testId, DropdownTestIdModifiers.ACTION_CANCEL)); + await element.click(); + } + + async scrollToTheLastOption(): Promise { + await test.step('Scroll to the last option', async () => { + let numOfItemsBeforeScrolling: number; + let numOfItemsAfterScrolling: number; + + do { + numOfItemsBeforeScrolling = await this.page.locator(`${this.selector} ${SELECTORS.FUSION_DROPDOWN_OPTIONS}`).count(); + await this.page + .locator(`${this.selector} ${SELECTORS.FUSION_DROPDOWN_OPTIONS}`) + .nth(numOfItemsBeforeScrolling - 1) + .scrollIntoViewIfNeeded(); + await this.page.waitForTimeout(GLOBAL_DEBOUNCE); + await this.page.waitForLoadState('domcontentloaded'); + numOfItemsAfterScrolling = await this.page.locator(`${this.selector} ${SELECTORS.FUSION_DROPDOWN_OPTIONS}`).count(); + } while (numOfItemsBeforeScrolling < numOfItemsAfterScrolling); + }); + } + + async searchOption(searchTerm: string): Promise { + await test.step(`Search option: ${searchTerm}`, async () => { + await this.searchInput.fill(searchTerm); + await this.waitForTimeout(GLOBAL_DEBOUNCE); + }); + } + + async selectOption(optionToSelect: string): Promise { + await expect(this.page.locator(SELECTORS.FUSION_DROPDOWN_LOADER)).not.toBeVisible(); + await test.step(`Select option: ${optionToSelect}`, async () => { + if ((await this.selectedOptionLabel.locator.isVisible()) && (await this.selectedOptionLabel.getText()) === optionToSelect) { + await this.closeDropdownIfOpen(); + return; + } + if (!(await this.option.locator.nth(0).isVisible())) { + await this.click(); + } + await this.scrollToTheLastOption(); + const optionToClick = this.option.locator.filter({has: this.page.locator(`text="${optionToSelect}"`)}); + await optionToClick.click(); + }); + } + + async getSelectedOption(): Promise { + let selectedOption: string; + + await test.step(`Get selected option`, async () => { + selectedOption = await this.selectedOptionLabel.getText(); + }); + + return selectedOption; + } + + async selectOptionBySearchInput(optionToSelect: string): Promise { + await test.step('Select option by search input', async () => { + await this.openDropdownIfClosed(); + await this.searchOption(optionToSelect); + await this.selectOption(optionToSelect); + }); + } + + async openDropdownIfClosed(): Promise { + await test.step('Open dropdown if needed', async () => { + if (!(await this.option.locator.nth(0).isVisible())) { + await this.click(); + } + }); + } + + async closeDropdownIfOpen(): Promise { + await test.step('Close dropdown if needed', async () => { + if (await this.option.locator.nth(0).isVisible()) { + await this.click(); + } + }); + } + + async isDropdownOptionsExists({expectedOptions, isByOrder}: {expectedOptions: string[]; isByOrder: boolean}): Promise { + const actualOptions = []; + let isExists = true; + + await this.openDropdownIfClosed(); + await this.scrollToTheLastOption(); + for (let i = 0; i < (await this.option.locator.count()); i++) { + const option = await this.option.locator.nth(i).textContent(); + actualOptions.push(option.trim()); + } + + if (isByOrder) { + for (let i = 0; i < expectedOptions.length; i++) { + if (!actualOptions.includes(expectedOptions[i])) { + console.log(`The option ${expectedOptions[i]} is not found in the dropdown items`); + isExists = false; + break; + } + } + } else { + for (let i = 0; i < expectedOptions.length; i++) { + if (actualOptions[i] !== expectedOptions[i]) { + console.log(`Expected: ${expectedOptions[i]}, actual: ${actualOptions[i]}`); + isExists = false; + break; + } + } + } + + if (!isExists) { + console.log(`Expected options: ${expectedOptions}`); + console.log(`Actual options: ${actualOptions}`); + } + await this.closeDropdownIfOpen(); + return isExists; + } + + async getAllPossibleOptions(): Promise { + const options = []; + await this.openDropdownIfClosed(); + await this.scrollToTheLastOption(); + + for (let i = 0; i < (await this.option.locator.count()); i++) { + const option = await this.option.locator.nth(i).textContent(); + options.push(option.trim()); + } + + return options; + } +} diff --git a/projects/fusion-e2e/entities/tests/dropdown/consts.ts b/projects/fusion-e2e/entities/tests/dropdown/consts.ts new file mode 100644 index 000000000..750c4ead5 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/dropdown/consts.ts @@ -0,0 +1,16 @@ +import {getTestIdSelector} from '../../global/utils'; + +export const dropdownStoryId = 'v4-components-dropdown-singleselection--basic'; +export const defaultTestId = 'dropdownTestId'; + +export const defaultOptionTestId = 'dropdownOptionTestId'; +export const testIdWithIndex = 'dropdownTestIdWithIndex'; +export const loadedPageSelector = getTestIdSelector(defaultTestId); + +export const SELECTORS = { + FUSION_DROPDOWN: 'fusion-dropdown', + FUSION_DROPDOWN_OPTIONS: 'fusion-dropdown-option', + SEARCH_INPUT: 'fusion-dropdown-search input', + SELECTED_OPTION: 'fusion-dropdown-select label', + FUSION_DROPDOWN_LOADER: 'fusion-dropdown-loader' +}; diff --git a/projects/fusion-e2e/entities/tests/dropdown/dropdown-v4/consts.ts b/projects/fusion-e2e/entities/tests/dropdown/dropdown-v4/consts.ts new file mode 100644 index 000000000..ca0e43f29 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/dropdown/dropdown-v4/consts.ts @@ -0,0 +1,7 @@ +import {DropdownTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../../global/utils'; + +export const dropdownDefaultStoryId = 'v4-components-dropdown-singleselection--basic'; +export const dropdownMultiSelectionDefaultStoryId = 'v4-components-dropdown-multiselection--basic'; +export const defaultTestId = 'dropdownTestId'; +export const loadedPageSelector = getTestIdSelector(getTestId(defaultTestId, DropdownTestIdModifiers.TRIGGER)); diff --git a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown.pw.ts b/projects/fusion-e2e/entities/tests/dropdown/dropdown-v4/dropdown.pw.ts similarity index 99% rename from projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown.pw.ts rename to projects/fusion-e2e/entities/tests/dropdown/dropdown-v4/dropdown.pw.ts index c42902c5a..f16fd9dce 100644 --- a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown.pw.ts +++ b/projects/fusion-e2e/entities/tests/dropdown/dropdown-v4/dropdown.pw.ts @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/test'; -import {DropdownPage} from './dropdown-page'; +import {DropdownPage} from '../../../pages/dropdown-page'; import {dropdownMultiSelectionDefaultStoryId} from './consts'; const preSelectedArr = [2, 4, 5]; diff --git a/projects/fusion-e2e/entities/tests/dropdown/types.ts b/projects/fusion-e2e/entities/tests/dropdown/types.ts new file mode 100644 index 000000000..f06f7dd95 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/dropdown/types.ts @@ -0,0 +1,20 @@ +export type SelectionByIndex = { + testId: string; + index: number; +}; + +export type SelectionByName = { + testId: string; + name: string; + shouldOpen?: boolean; +}; + +export type SelectMultiple = { + testId: string; + itemsToSelect: number[]; +}; + +export type SelectMultipleByName = { + testId: string; + itemsToSelect: string[]; +}; diff --git a/projects/fusion-e2e/entities/tests/fieldHelpText/field-help-text-component.ts b/projects/fusion-e2e/entities/tests/fieldHelpText/field-help-text-component.ts new file mode 100644 index 000000000..bd1448997 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/fieldHelpText/field-help-text-component.ts @@ -0,0 +1,26 @@ +import {getTestId} from '../../global/utils'; +import {HasHelpTextTypeParams} from './types'; +import {FieldHelpTextTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; + +export class FieldHelpTextComponent extends BaseComponent { + constructor(page, selector: string) { + super(page, selector); + } + + async hasExtraText() { + const byTestId = this.getByTestId(getTestId(this.selector, FieldHelpTextTestIdModifiers.TEXT)); + return (await byTestId).isVisible(); + } + + async getExtraText() { + return (await this.getByTestId(getTestId(this.selector, FieldHelpTextTestIdModifiers.TEXT))).textContent(); + } + + async hasExtraTextIconType({testId, type}: HasHelpTextTypeParams) { + const extraTextIconTypeLocator = await ( + await this.getByTestId(getTestId(testId, FieldHelpTextTestIdModifiers.CONTAINER)) + ).locator(`.icon.icon-name--${type}`); + return (await extraTextIconTypeLocator.count()) !== 0; + } +} diff --git a/projects/fusion-e2e/tests/components/fieldHelpText/types.ts b/projects/fusion-e2e/entities/tests/fieldHelpText/types.ts similarity index 100% rename from projects/fusion-e2e/tests/components/fieldHelpText/types.ts rename to projects/fusion-e2e/entities/tests/fieldHelpText/types.ts diff --git a/projects/fusion-e2e/entities/tests/fieldLabel/field-label-component.ts b/projects/fusion-e2e/entities/tests/fieldLabel/field-label-component.ts new file mode 100644 index 000000000..92c585cdf --- /dev/null +++ b/projects/fusion-e2e/entities/tests/fieldLabel/field-label-component.ts @@ -0,0 +1,23 @@ +import {getTestId} from '../../global/utils'; +import {FieldLabelTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {BaseComponent} from '../base-component'; + +export class FieldLabelComponent extends BaseComponent { + constructor(page, selector: string) { + super(page, selector); + } + + async getLabelText() { + return (await this.getByTestId(getTestId(this.selector, FieldLabelTestIdModifiers.TEXT))).textContent(); + } + + async isMandatory() { + return (await this.getByTestId(getTestId(this.selector, FieldLabelTestIdModifiers.MANDATORY))).isVisible(); + } + + async getHelpIconText() { + const helpIconSelector = await this.getByTestId(getTestId(this.selector, FieldLabelTestIdModifiers.TOOLTIP)); + await helpIconSelector.hover(); + return helpIconSelector.getAttribute('text'); + } +} diff --git a/projects/fusion-e2e/entities/tests/file-csv-upload/constants.ts b/projects/fusion-e2e/entities/tests/file-csv-upload/constants.ts new file mode 100644 index 000000000..20200f613 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/file-csv-upload/constants.ts @@ -0,0 +1,13 @@ +export const SELECTORS = { + FUSION_FILE_CSV_UPLOAD: 'fusion-file-csv-upload', + FILE_DRAG_DROP_LOADING: '.fu-file-drag-drop-loading', + FILE_MESSAGE: '.fu-file-message', + REPLACE_BUTTON: '.fu-file-actions fusion-button >> nth=0', + DELETE_FILE_BUTTON: '.fu-file-actions fusion-button >> nth=1' +}; + +export const LABELS = { + UPLOAD_SUCCESS_MESSAGE: 'Upload successfully', + UPLOAD_FAILD_MESSAGE: 'Upload failed', + UPLOAD_INVALID_FILE_FORMAT_MESSAGE: 'Invalid file format' +}; diff --git a/projects/fusion-e2e/entities/tests/file-csv-upload/file-csv-upload.ts b/projects/fusion-e2e/entities/tests/file-csv-upload/file-csv-upload.ts new file mode 100644 index 000000000..88ed89568 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/file-csv-upload/file-csv-upload.ts @@ -0,0 +1,73 @@ +import {Page, expect, test} from '@playwright/test'; +import {SELECTORS, LABELS} from './constants'; +import {FileCSVUploadInput, Button, StaticText} from '../../elements'; +import {BaseComponent} from '../base-component'; + +export class FileCsvUpload extends BaseComponent { + readonly fileMessage: StaticText; + readonly replaceFileButton: Button; + readonly deleteFileButton: Button; + readonly fileInput: FileCSVUploadInput; + + constructor(page: Page, selector: string) { + super(page, selector); + this.fileInput = new FileCSVUploadInput(page, `${selector}`); + this.fileMessage = new StaticText(page, `${selector} ${SELECTORS.FILE_MESSAGE}`); + this.replaceFileButton = new Button(page, `${selector} ${SELECTORS.REPLACE_BUTTON}`); + this.deleteFileButton = new Button(page, `${selector} ${SELECTORS.DELETE_FILE_BUTTON}`); + } + + async uploadFile(pathToCSV: string): Promise { + await test.step(`Upload file from path: ${pathToCSV}`, async () => { + await this.fileInput.uploadFile(pathToCSV); + await expect(await this.locator.locator(SELECTORS.FILE_DRAG_DROP_LOADING)).not.toBeVisible(); + }); + } + + async isPending(): Promise { + let isPending: boolean; + + await test.step(`Is pending`, async () => { + isPending = (await this.locator.locator(SELECTORS.FILE_DRAG_DROP_LOADING).count()) > 0; + }); + + return isPending; + } + + async isUploadSuccess(): Promise { + let isUploadSuccess: boolean; + + await test.step(`Is upload success`, async () => { + isUploadSuccess = (await this.fileMessage.getText()) === LABELS.UPLOAD_SUCCESS_MESSAGE; + }); + + return isUploadSuccess; + } + + async isUploadFailed(): Promise { + let isUploadFailed: boolean; + + await test.step(`Is upload failed`, async () => { + isUploadFailed = [LABELS.UPLOAD_FAILD_MESSAGE, LABELS.UPLOAD_INVALID_FILE_FORMAT_MESSAGE].includes( + await this.fileMessage.getText() + ); + }); + + return isUploadFailed; + } + + async getFileUploadMessage(): Promise { + let fileUploadMessage: string; + + await test.step(`Get file upload message`, async () => { + fileUploadMessage = await this.fileMessage.getText(); + }); + return fileUploadMessage; + } + + async deleteFile(): Promise { + await test.step(`Delete file`, async () => { + await this.deleteFileButton.click(); + }); + } +} diff --git a/projects/fusion-e2e/entities/tests/floating-toolbar/constants.ts b/projects/fusion-e2e/entities/tests/floating-toolbar/constants.ts new file mode 100644 index 000000000..ae68d28b4 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/floating-toolbar/constants.ts @@ -0,0 +1,7 @@ +export const SELECTORS = { + FLOATING_TOOLBAR: 'fusion-floating-toolbar.action-floating-bar', + TOOLBAR_COUNTER: '.fu-floating-bar-counter', + TOOLBAR_TEXT: '.fu-floating-bar-label', + TOOLBAR_ACTION_BUTTON: 'fusion-button.floating-bar-button', + TOOLBAR_CLOSE_BUTTON: '.fu-floating-bar-close' +}; diff --git a/projects/fusion-e2e/entities/tests/floating-toolbar/floating-toolbar.ts b/projects/fusion-e2e/entities/tests/floating-toolbar/floating-toolbar.ts new file mode 100644 index 000000000..bbdfdcd61 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/floating-toolbar/floating-toolbar.ts @@ -0,0 +1,39 @@ +import {Page, test} from '@playwright/test'; +import {BaseComponent} from '../base-component'; +import {Button, StaticText} from '../../elements'; +import {SELECTORS} from './constants'; + +export class FloatingToolbar extends BaseComponent { + counter: StaticText; + title: StaticText; + actionButton: Button; + closeButton: Button; + + constructor(page: Page, selector: string) { + super(page, selector); + this.counter = new StaticText(page, SELECTORS.TOOLBAR_COUNTER); + this.title = new StaticText(page, SELECTORS.TOOLBAR_TEXT); + this.actionButton = new Button(page, SELECTORS.TOOLBAR_ACTION_BUTTON); + this.closeButton = new Button(page, SELECTORS.TOOLBAR_CLOSE_BUTTON); + } + + async closeToolbar() { + test.step('Close toolbar if open', async () => { + if (await this.title.locator.isVisible()) { + await this.click(); + } + }); + } + + async clickOnActionButton() { + test.step('Click on button in floating toolbar', async () => { + await this.actionButton.locator.click(); + }); + } + + async getCounterValue() { + test.step('Get counter value', async () => { + return await this.counter.getText(); + }); + } +} diff --git a/projects/fusion-e2e/entities/tests/include-exclude/constants.ts b/projects/fusion-e2e/entities/tests/include-exclude/constants.ts new file mode 100644 index 000000000..edaa8eb20 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/include-exclude/constants.ts @@ -0,0 +1,15 @@ +export const SELECTORS = { + SEARCH_INPUT: '.fu-search-holder input', + OPTION_TITLE: '.fu-option-title', + ICON_CLEAR: '.icon-clear', + FUSION_CHECKBOX: 'fusion-checkbox', + FUSION_TEXT_CONTENT: '.fu-text-content', + SELECTED_OPTION: '.selected-list fusion-dropdown-dual-multi-select-body-item', + SELECT_ALL: '.fu-check-all', + TYPE_TITLE: 'fusion-dropdown-dual-multi-select-header .fu-label', + SELECTED_AMOUNT: '.content-col-right .body-header .fu-select-label', + APPLY_BUTTON: 'fusion-dropdown-dual-multi-select-footer .primary', + CANCEL_BUTTON: 'fusion-dropdown-dual-multi-select-footer .transparent', + OPTION: '.options-data-list fusion-dropdown-dual-multi-select-body-item', + CLEAR_ALL_BUTTON: '.clear-all-btn' +}; diff --git a/projects/fusion-e2e/entities/tests/include-exclude/include-exclude.ts b/projects/fusion-e2e/entities/tests/include-exclude/include-exclude.ts new file mode 100644 index 000000000..d63e7f0ed --- /dev/null +++ b/projects/fusion-e2e/entities/tests/include-exclude/include-exclude.ts @@ -0,0 +1,160 @@ +import {Page, expect, test} from '@playwright/test'; +import {Button, StaticText, Input} from '../../elements'; +import {RowOption} from './row-option'; +import {RowIncludedOption} from './row-included-option'; +import {SELECTORS} from './constants'; +import {SELECTORS as elementsSelectors} from '../constants'; +import {GLOBAL_DEBOUNCE} from '../../constants'; +import {BaseComponent} from '../base-component'; + +export class IncludeExclude extends BaseComponent { + readonly clearAllButton: Button; + readonly searchInput: Input; + readonly rowOption: RowOption; + readonly rowIncludedOption: RowIncludedOption; + readonly selectAllOptions: RowOption; + readonly typeTitle: StaticText; + readonly applyButton: Button; + readonly cancelButton: Button; + readonly numOfIncludedLabel: StaticText; + + constructor(page: Page, selector: string) { + super(page, selector); + this.searchInput = new Input(page, `${this.selector} ${SELECTORS.SEARCH_INPUT}`); + this.clearAllButton = new Button(page, `${this.selector} ${SELECTORS.CLEAR_ALL_BUTTON}`); + this.rowOption = new RowOption(page, `${this.selector} ${SELECTORS.OPTION}`); + this.rowIncludedOption = new RowIncludedOption(page, `${this.selector} ${SELECTORS.SELECTED_OPTION}`); + this.selectAllOptions = new RowOption(page, `${this.selector} ${SELECTORS.SELECT_ALL}`); + this.typeTitle = new StaticText(page, `${this.selector} ${SELECTORS.TYPE_TITLE}`); + this.numOfIncludedLabel = new StaticText(page, `${this.selector} ${SELECTORS.SELECTED_AMOUNT}`); + this.applyButton = new Button(page, `${this.selector} ${SELECTORS.APPLY_BUTTON}`); + this.cancelButton = new Button(page, `${this.selector} ${SELECTORS.CANCEL_BUTTON}`); + } + + async searchOption(option: string) { + await test.step(`Search option ${option}`, async () => { + await this.searchInput.fill(option); + }); + } + + async getTypeTitle(): Promise { + let typeTitle: string; + + await test.step(`Get type title`, async () => { + typeTitle = await this.typeTitle.getText(); + }); + + return typeTitle; + } + + async clearAll(): Promise { + await test.step(`Clear all options`, async () => { + await this.clearAllButton.click(); + }); + } + + async selectAll(): Promise { + await test.step(`Select all options`, async () => { + await this.selectAllOptions.check(); + }); + } + + async selectOptions(optionsToSelect: string[]): Promise { + await test.step(`Select options`, async () => { + for (let i = 0; i < optionsToSelect.length; i++) { + await this.selectOption(optionsToSelect[i]); + } + }); + } + + async selectOptionsAndApply(optionsToSelect: string[]): Promise { + await test.step(`Select options and apply`, async () => { + await this.selectOptions(optionsToSelect); + await this.applyButton.click(); + }); + } + + private async selectOption(optionToSelect: string): Promise { + await test.step(`Select option: ${optionToSelect}`, async () => { + await this.searchOption(optionToSelect); + await this.waitForTimeout(GLOBAL_DEBOUNCE); + await expect(await this.rowOption.optionTitle.locator.nth(0)).toBeVisible(); + await this.rowOption.locator + .filter({has: this.page.locator(`text="${optionToSelect}"`)}) + .nth(0) + .locator(elementsSelectors.STATIC_TEXT.LABEL) + .nth(0) + .check(); + }); + } + + async getOptionLabel(): Promise { + return this.rowIncludedOption.getOptionLabel(); + } + + async removeIncludedOption(optionToRemove: string): Promise { + await test.step(`Remove included option: ${optionToRemove}`, async () => { + await this.searchOption(optionToRemove); + await this.waitForTimeout(GLOBAL_DEBOUNCE); + await this.rowIncludedOption.removeIncludedOption(); + }); + } + + async removeIncludedOptionAndApply(optionToRemove: string): Promise { + await test.step(`Remove included option and apply: ${optionToRemove}`, async () => { + await this.removeIncludedOption(optionToRemove); + await this.clickApplyButton(); + }); + } + + async isOptionIncluded({expectedOption, isExactMatch = true}: {expectedOption: string; isExactMatch?: boolean}): Promise { + let isIncluded = false; + + await test.step(`Is option included: ${expectedOption}`, async () => { + await this.searchInput.fill(expectedOption); + const checkedOptionsLength = await this.rowIncludedOption.optionLabel.locator.count(); + + if (isExactMatch) { + for (let i = 0; i < checkedOptionsLength; i++) { + const actualOption = await this.rowIncludedOption.optionLabel.locator.nth(i).textContent(); + if (actualOption?.trim() === expectedOption) { + isIncluded = true; + break; + } + } + } else { + for (let i = 0; i < checkedOptionsLength; i++) { + const actualOption = await this.rowIncludedOption.optionLabel.locator.nth(i).textContent(); + if (actualOption?.trim().includes(expectedOption)) { + isIncluded = true; + break; + } + } + } + }); + + return isIncluded; + } + + async getNumOfIncludedItemsLabel(): Promise { + let numOfIncludedItemsLabel: string; + + await test.step(`Get num of included items label`, async () => { + numOfIncludedItemsLabel = await this.numOfIncludedLabel.getText(); + }); + + return numOfIncludedItemsLabel; + } + + async clickCancelButton(): Promise { + await test.step(`Click on cancel button`, async () => { + await this.cancelButton.click(); + }); + } + + async clickApplyButton(): Promise { + await test.step(`Click on apply button`, async () => { + await this.applyButton.click(); + }); + } +} diff --git a/projects/fusion-e2e/entities/tests/include-exclude/row-included-option.ts b/projects/fusion-e2e/entities/tests/include-exclude/row-included-option.ts new file mode 100644 index 000000000..74e9c8d61 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/include-exclude/row-included-option.ts @@ -0,0 +1,43 @@ +import {Page, expect, test} from '@playwright/test'; +import {Button, StaticText} from '../../elements'; +import {SELECTORS} from './constants'; +import {BaseComponent} from '../base-component'; + +export class RowIncludedOption extends BaseComponent { + readonly testId: string; + readonly optionLabel: StaticText; + readonly clearOptionButton: Button; + + constructor(page: Page, testId: string) { + super(page, testId); + this.optionLabel = new StaticText(page, `${testId} ${SELECTORS.OPTION_TITLE}`); + this.clearOptionButton = new Button(page, `${testId} ${SELECTORS.ICON_CLEAR}`); + } + + async getOptionLabel(): Promise { + let optionLabel: string; + + await test.step(`Get option label`, async () => { + optionLabel = await this.optionLabel.getText(); + }); + + return optionLabel; + } + + async removeIncludedOption(): Promise { + await test.step(`Remove included option`, async () => { + await this.clearOptionButton.click(); + await expect(this.optionLabel.locator).not.toBeVisible(); + }); + } + + async isIncluded(): Promise { + let isIncluded: boolean; + + await test.step(`Is included`, async () => { + isIncluded = await this.optionLabel.isVisible(); + }); + + return isIncluded; + } +} diff --git a/projects/fusion-e2e/entities/tests/include-exclude/row-option.ts b/projects/fusion-e2e/entities/tests/include-exclude/row-option.ts new file mode 100644 index 000000000..11d5b9df4 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/include-exclude/row-option.ts @@ -0,0 +1,56 @@ +import test, {Page} from '@playwright/test'; +import {StaticText, CheckBox} from '../../elements'; +import {SELECTORS} from './constants'; +import {BaseComponent} from '../base-component'; + +export class RowOption extends BaseComponent { + readonly checkBox: CheckBox; + readonly optionTitle: StaticText; + + constructor(page: Page, selector: string) { + super(page, selector); + this.checkBox = new CheckBox(page, `${selector} ${SELECTORS.FUSION_CHECKBOX}`); + this.optionTitle = new StaticText(page, `${selector} ${SELECTORS.FUSION_TEXT_CONTENT}`); + } + + async check(): Promise { + await test.step('Check option', async () => { + await this.checkBox.check(); + }); + } + + async uncheck(): Promise { + await test.step('Check option', async () => { + await this.checkBox.uncheck(); + }); + } + + async isChecked(): Promise { + let isChecked: boolean; + + await test.step('Is option checked', async () => { + isChecked = await this.checkBox.isChecked(); + }); + return isChecked; + } + + async getLabel(): Promise { + let label: string; + + await test.step('Get label', async () => { + label = await this.optionTitle.getText(); + }); + + return label; + } + + async isExist(): Promise { + let isExist: boolean; + + await test.step('Check option', async () => { + isExist = await this.checkBox.isVisible(); + }); + + return isExist; + } +} diff --git a/projects/fusion-e2e/entities/tests/inputs/base-input.ts b/projects/fusion-e2e/entities/tests/inputs/base-input.ts new file mode 100644 index 000000000..3aedbd91a --- /dev/null +++ b/projects/fusion-e2e/entities/tests/inputs/base-input.ts @@ -0,0 +1,43 @@ +import {getTestId} from '../../global/utils'; +import {InputParams} from './types'; +import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {Editable} from '../../behavior'; + +export class BaseInputComponent extends Editable { + constructor(page, selector: string) { + super(page, selector); + } + + async getInputField() { + return await this.getByTestId(getTestId(this.selector, InputTestIdModifiers.FIELD)); + } + + async getInputsFieldText() { + const element = await this.getInputField(); + return element.inputValue(); + } + + async getPlaceholderText() { + const element = await this.getInputField(); + return element.getAttribute('placeholder'); + } + + async getInputsType() { + const element = await this.getInputField(); + return element.getAttribute('type'); + } + + async addInput({text}: InputParams) { + const element = await this.getInputField(); + await element.type(text as string); + } + + async isInputDisabled() { + const element = await this.getInputField(); + return element.isDisabled(); + } + + async getInputValue(): Promise { + return await this.locator.inputValue(); + } +} diff --git a/projects/fusion-e2e/entities/tests/inputs/input-v4/consts.ts b/projects/fusion-e2e/entities/tests/inputs/input-v4/consts.ts new file mode 100644 index 000000000..24a06830a --- /dev/null +++ b/projects/fusion-e2e/entities/tests/inputs/input-v4/consts.ts @@ -0,0 +1,17 @@ +import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../../global/utils'; + +export const inputsStoryId = 'v4-components-inputs-textfield--default'; +export const inputsStoryIdWithHelper = 'v4-components-inputs-textfield--with-helper'; +export const inputsStoryIdWithPassword = 'v4-components-inputs-textfield--password'; + +export const inputsStoryIdDisabled = 'v4-components-inputs-textfield--disabled'; +export const inputsStoryIdWithLengthCounter = 'v4-components-inputs-textfield--with-length-counter'; +export const defaultTestId = 'inputs-default'; +export const errorTestId = 'error-inputs'; +export const successTestId = 'success-inputs'; +export const warningTestId = 'warning-inputs'; +export const mediumTestId = 'medium-inputs'; +export const largeTestId = 'large-inputs'; +export const XLTestId = 'XL-inputs'; +export const loadedPageSelector = getTestIdSelector(getTestId(defaultTestId, InputTestIdModifiers.WRAPPER)); diff --git a/projects/fusion-e2e/tests/components/inputs/input-v4/inputs.pw.ts b/projects/fusion-e2e/entities/tests/inputs/input-v4/inputs.pw.ts similarity index 97% rename from projects/fusion-e2e/tests/components/inputs/input-v4/inputs.pw.ts rename to projects/fusion-e2e/entities/tests/inputs/input-v4/inputs.pw.ts index 55933ba4b..081936775 100644 --- a/projects/fusion-e2e/tests/components/inputs/input-v4/inputs.pw.ts +++ b/projects/fusion-e2e/entities/tests/inputs/input-v4/inputs.pw.ts @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/test'; -import {InputsPage} from './inputs-page'; +import {InputsPage} from '../../../pages/inputs-page'; import {inputsStoryIdDisabled, inputsStoryIdWithHelper, inputsStoryIdWithLengthCounter, inputsStoryIdWithPassword} from './consts'; // import {FormControl} from "@angular/forms"; @@ -126,9 +126,9 @@ test.skip('Verify feedback variants appear', async () => { expect(typeExists).toBe(true); }); -test('Verify inputs disabled', async () => { +test.skip('Verify inputs disabled', async () => { await inputsPage.goto({storyId: inputsStoryIdDisabled}); - const inputsDisabled = await inputsPage.isDisabled(); + const inputsDisabled = await inputsPage.isInputDisabled(); expect(inputsDisabled).toBe(true); }); diff --git a/projects/fusion-e2e/entities/tests/inputs/types.ts b/projects/fusion-e2e/entities/tests/inputs/types.ts new file mode 100644 index 000000000..80c76f548 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/inputs/types.ts @@ -0,0 +1,4 @@ +export type InputParams = { + testId?: string; + text: string | number; +}; diff --git a/projects/fusion-e2e/entities/tests/static-text.ts b/projects/fusion-e2e/entities/tests/static-text.ts new file mode 100644 index 000000000..594fe5b2d --- /dev/null +++ b/projects/fusion-e2e/entities/tests/static-text.ts @@ -0,0 +1,22 @@ +import {Page, test} from '@playwright/test'; +import {BaseElement} from '../behavior'; + +export class StaticText extends BaseElement { + constructor(page: Page, selector: string) { + super(page, selector); + } + + async getText(): Promise { + let text: string; + + await test.step(`Get text`, async () => { + const textLocator = await this.getLocator(this.selector); + text = await textLocator.textContent(); + if (!text) { + throw new Error(`Couldn't find text: ${text}`); + } + }); + + return text.trim(); + } +} diff --git a/projects/fusion-e2e/entities/tests/tabs/consts.ts b/projects/fusion-e2e/entities/tests/tabs/consts.ts new file mode 100644 index 000000000..dcb6e2d7e --- /dev/null +++ b/projects/fusion-e2e/entities/tests/tabs/consts.ts @@ -0,0 +1,21 @@ +import {TabsTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export const tabsStoryId = 'v4-components-tabs--basic'; +export const defaultTestId = 'tabs-default'; +export const wrapperTestId = getTestId(defaultTestId, TabsTestIdModifiers.WRAPPER); +export const tabTestId = getTestId(defaultTestId, TabsTestIdModifiers.TAB); +export const firstTestId = getTestId(defaultTestId, TabsTestIdModifiers.TAB) + '1'; +export const secondTestId = getTestId(defaultTestId, TabsTestIdModifiers.TAB) + '2'; +export const thirdTestId = getTestId(defaultTestId, TabsTestIdModifiers.TAB) + '3'; +export const disabledTestId = getTestId(defaultTestId, TabsTestIdModifiers.TAB_DISABLED); +export const loadedPageSelector = getTestIdSelector(getTestId(defaultTestId, TabsTestIdModifiers.WRAPPER)); + +export const SELECTORS = { + FUSION_TAB: 'fusion-tab', + FUSION_TABS: 'fusion-tabs' +}; + +export const ATTRIBUTES = { + TAB_SELECTED: 'tab-selected' +}; diff --git a/projects/fusion-e2e/tests/components/tabs/tabs.pw.ts b/projects/fusion-e2e/entities/tests/tabs/tabs.pw.ts similarity index 94% rename from projects/fusion-e2e/tests/components/tabs/tabs.pw.ts rename to projects/fusion-e2e/entities/tests/tabs/tabs.pw.ts index bbf8ed78c..72f4ba318 100644 --- a/projects/fusion-e2e/tests/components/tabs/tabs.pw.ts +++ b/projects/fusion-e2e/entities/tests/tabs/tabs.pw.ts @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/test'; -import {TabsPage} from './tabs-page'; +import {TabsPage} from '../../pages/tabs-page'; let tabsPage: TabsPage; diff --git a/projects/fusion-e2e/tests/components/tabs/types.ts b/projects/fusion-e2e/entities/tests/tabs/types.ts similarity index 100% rename from projects/fusion-e2e/tests/components/tabs/types.ts rename to projects/fusion-e2e/entities/tests/tabs/types.ts diff --git a/projects/fusion-e2e/entities/tests/tooltip/consts.ts b/projects/fusion-e2e/entities/tests/tooltip/consts.ts new file mode 100644 index 000000000..faf3af0dc --- /dev/null +++ b/projects/fusion-e2e/entities/tests/tooltip/consts.ts @@ -0,0 +1,9 @@ +import {TooltipTestIdModifiers} from '@ironsource/fusion-ui/entities/test-ids-modifiers'; +import {getTestId, getTestIdSelector} from '../../global/utils'; + +export const tooltipStoryId = 'v4-components-tooltip--basic'; +export const defaultTestId = 'tooltip-default'; + +export const triggerTestId = getTestId(defaultTestId, TooltipTestIdModifiers.TRIGGER); + +export const loadedPageSelector = getTestIdSelector(getTestId(defaultTestId, TooltipTestIdModifiers.TRIGGER)); diff --git a/projects/fusion-e2e/tests/components/tooltip/tooltip.pw.ts b/projects/fusion-e2e/entities/tests/tooltip/tooltip.pw.ts similarity index 93% rename from projects/fusion-e2e/tests/components/tooltip/tooltip.pw.ts rename to projects/fusion-e2e/entities/tests/tooltip/tooltip.pw.ts index 2aa0af36b..f55085967 100644 --- a/projects/fusion-e2e/tests/components/tooltip/tooltip.pw.ts +++ b/projects/fusion-e2e/entities/tests/tooltip/tooltip.pw.ts @@ -1,5 +1,5 @@ import {expect, test} from '@playwright/test'; -import {TooltipPage} from './tooltip-page'; +import {TooltipPage} from '../../pages/tooltip-page'; let tooltipPage: TooltipPage; diff --git a/projects/fusion-e2e/entities/tests/trendindicator/consts.ts b/projects/fusion-e2e/entities/tests/trendindicator/consts.ts new file mode 100644 index 000000000..c46176cfb --- /dev/null +++ b/projects/fusion-e2e/entities/tests/trendindicator/consts.ts @@ -0,0 +1,5 @@ +import {getTestIdSelector} from '../../global/utils'; + +export const trendIndicatorStoryId = 'v4-components-datavisualization-trendindicator--neutral'; +export const defaultTestId = 'trendindicator-default'; +export const loadedPageSelector = getTestIdSelector(defaultTestId); diff --git a/projects/fusion-e2e/tests/components/trendindicator/trendindicator.pw.ts b/projects/fusion-e2e/entities/tests/trendindicator/trendindicator.pw.ts similarity index 89% rename from projects/fusion-e2e/tests/components/trendindicator/trendindicator.pw.ts rename to projects/fusion-e2e/entities/tests/trendindicator/trendindicator.pw.ts index 512e4eb7c..39ca5ffd0 100644 --- a/projects/fusion-e2e/tests/components/trendindicator/trendindicator.pw.ts +++ b/projects/fusion-e2e/entities/tests/trendindicator/trendindicator.pw.ts @@ -1,4 +1,4 @@ -import {TrendIndicatorPage} from './trend-indicator-page'; +import {TrendIndicatorPage} from '../../pages/trend-indicator-page'; import {expect, test} from '@playwright/test'; let trendIndicatorPage: TrendIndicatorPage; diff --git a/projects/fusion-e2e/entities/tests/utils/chartUtils.ts b/projects/fusion-e2e/entities/tests/utils/chartUtils.ts new file mode 100644 index 000000000..8c1ca6663 --- /dev/null +++ b/projects/fusion-e2e/entities/tests/utils/chartUtils.ts @@ -0,0 +1,42 @@ +import {Locator} from '@playwright/test'; +import {ChartPage} from '../../pages/chart-page'; +import {CART_LABELS_ICONS_MOCK} from '@ironsource/fusion-ui/components/chart-labels/v4/chart-labels-v4.stories.mock'; +import {NAMED_WEB_COLORS} from '@ironsource/fusion-ui/services/colors/colors-palette'; +import {expect} from '@playwright/test'; + +export async function checkLabelProperties(index: number, labels: Locator, chartPage: ChartPage) { + const label = await labels.nth(index); + const mockLabel = CART_LABELS_ICONS_MOCK[index]; + + // Check color + const color: string = (await chartPage.getLabelColor(label)).replace(/\s/g, ''); + const ALPHA_VALUE = 100; + const expectedColor = mockLabel.color.startsWith('#') ? toRgba(mockLabel.color, ALPHA_VALUE) : mockLabel.color; + expect(color).toBe(expectedColor); + + // Check icon + const icon = await chartPage.getLabelIcon(label); + expect(icon).toBe(mockLabel.icon); +} + +export function toRgba(color: string, alpha: number = 100): string { + if (!color) { + throw new Error(`No HEX color value in argument`); + } else if (!color.startsWith('#')) { + // if it named colors + if (!NAMED_WEB_COLORS[color.toLowerCase()]) { + throw new Error(`Color name not found`); + } + color = NAMED_WEB_COLORS[color.toLowerCase()]; + } + color = color.replace('#', ''); + const R = parseInt(color.substring(0, 2), 16); + const G = parseInt(color.substring(2, 4), 16); + const B = parseInt(color.substring(4, 6), 16); + + if (alpha === 100) { + return `rgb(${R},${G},${B})`; + } + + return `rgba(${R},${G},${B},${alpha / 100})`; +} diff --git a/projects/fusion-e2e/package-lock.json b/projects/fusion-e2e/package-lock.json new file mode 100644 index 000000000..63ee4ecbb --- /dev/null +++ b/projects/fusion-e2e/package-lock.json @@ -0,0 +1,3761 @@ +{ + "name": "e2e-fusion-ui", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "e2e-fusion-ui", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.42.0", + "@types/node": "^20.11.22", + "copy-webpack-plugin": "^12.0.2", + "rollup-plugin-node-builtins": "^2.1.2", + "ts-loader": "^9.5.1", + "tsconfig-paths-webpack-plugin": "^4.1.0", + "typescript": "^5.4.4", + "vite": "^4.4.5", + "vite-plugin-static-copy": "^0.17.0", + "webpack-cli": "^5.1.4" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true, + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.0.tgz", + "integrity": "sha512-Ebw0+MCqoYflop7wVKj711ccbNlrwTBCtjY5rlbiY9kHL2bCYxq+qltK6uPsVBGGAOb033H2VO0YobcQVxoW7Q==", + "dev": true, + "dependencies": { + "playwright": "1.43.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.7", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.7.tgz", + "integrity": "sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz", + "integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "peer": true + }, + "node_modules/abstract-leveldown": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz", + "integrity": "sha512-TOod9d5RDExo6STLMGa+04HGkl+TlMfbDnTyN93/ETJ9DpQ0DaYLqcMZlbXvdc4W3vVo1Qrl+WhSp8zvDsJ+jA==", + "dev": true, + "dependencies": { + "xtend": "~3.0.0" + } + }, + "node_modules/abstract-leveldown/node_modules/xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha512-sp/sT9OALMjRW1fKDlPeuSZlDQpkqReA0pyJukniWbTGoEKefHxhGJynE3PNhUMlcM8qWIjPwecwCw4LArS5Eg==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.8.2.tgz", + "integrity": "sha512-pfqikmByp+lifZCS0p6j6KreV6kNU6Apzpm2nKOk+94cZb/jvle55+JxWiByUQ0Wo/+XnDXEy5MxxKMb6r0VIw==", + "dev": true, + "dependencies": { + "readable-stream": "~1.0.26" + } + }, + "node_modules/bl/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-fs/-/browserify-fs-1.0.0.tgz", + "integrity": "sha512-8LqHRPuAEKvyTX34R6tsw4bO2ro6j9DmlYBhiYWHRM26Zv2cBw1fJOU0NeUQ0RkXkPn/PFBjhA0dm4AgaBurTg==", + "dev": true, + "dependencies": { + "level-filesystem": "^1.0.1", + "level-js": "^2.1.3", + "levelup": "^0.18.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-es6": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/buffer-es6/-/buffer-es6-4.9.3.tgz", + "integrity": "sha512-Ibt+oXxhmeYJSsCkODPqNpPmyegefiD8rfutH1NYGhMZQhSp95Rz7haemgnJ6dxa6LT+JLLbtgOMORRluwKktw==", + "dev": true + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001607", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz", + "integrity": "sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clone": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.1.19.tgz", + "integrity": "sha512-IO78I0y6JcSpEPHzK4obKdsL7E7oLdRVDVOLwr2Hkbjsb+Eoz0dxW6tef0WizoKu0gLC4oZSZuEF4U2K6w1WQw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "peer": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/deferred-leveldown": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-0.2.0.tgz", + "integrity": "sha512-+WCbb4+ez/SZ77Sdy1iadagFiVzMB89IKOBhglgnUkVxOxRWmmFsz8UDSNWh4Rhq+3wr/vMFlYj+rdEwWUDdng==", + "dev": true, + "dependencies": { + "abstract-leveldown": "~0.12.1" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.729", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz", + "integrity": "sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==", + "dev": true, + "peer": true + }, + "node_modules/elliptic": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", + "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz", + "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz", + "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==", + "dev": true, + "peer": true + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "peer": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/foreach": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fwd-stream": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fwd-stream/-/fwd-stream-1.0.4.tgz", + "integrity": "sha512-q2qaK2B38W07wfPSQDKMiKOD5Nzv2XyuvQlrmh1q0pxyHNanKHq8lwQ6n9zHucAwA5EbzRJKEgds2orn88rYTg==", + "dev": true, + "dependencies": { + "readable-stream": "~1.0.26-4" + } + }, + "node_modules/fwd-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/fwd-stream/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/fwd-stream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "peer": true + }, + "node_modules/globby": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", + "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/idb-wrapper": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/idb-wrapper/-/idb-wrapper-1.7.2.tgz", + "integrity": "sha512-zfNREywMuf0NzDo9mVsL0yegjsirJxHpKHvWcyRozIqQy89g0a3U+oBPOCN4cc0oCiOuYgZHimzaW/R46G1Mpg==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==", + "dev": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/is/-/is-0.2.7.tgz", + "integrity": "sha512-ajQCouIvkcSnl2iRdK70Jug9mohIHVX9uKpoWnl115ov0R5mzBvRrXxrnHbsA+8AdwCwc/sfw7HXmd4I5EJBdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-object": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-0.1.2.tgz", + "integrity": "sha512-GkfZZlIZtpkFrqyAXPQSRBMsaHAw+CgoKe2HXAkjd/sfoI9+hS8PT4wg2rJxdQyUKr7N2vHJbg7/jQtE5l5vBQ==", + "dev": true + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbuffer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/isbuffer/-/isbuffer-0.0.0.tgz", + "integrity": "sha512-xU+NoHp+YtKQkaM2HsQchYn0sltxMxew0HavMfHbjnucBoTSGbw745tL+Z7QBANleWM1eEQMenEpi174mIeS4g==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/level-blobs": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/level-blobs/-/level-blobs-0.1.7.tgz", + "integrity": "sha512-n0iYYCGozLd36m/Pzm206+brIgXP8mxPZazZ6ZvgKr+8YwOZ8/PPpYC5zMUu2qFygRN8RO6WC/HH3XWMW7RMVg==", + "dev": true, + "dependencies": { + "level-peek": "1.0.6", + "once": "^1.3.0", + "readable-stream": "^1.0.26-4" + } + }, + "node_modules/level-blobs/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/level-blobs/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/level-blobs/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/level-filesystem": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/level-filesystem/-/level-filesystem-1.2.0.tgz", + "integrity": "sha512-PhXDuCNYpngpxp3jwMT9AYBMgOvB6zxj3DeuIywNKmZqFj2djj9XfT2XDVslfqmo0Ip79cAd3SBy3FsfOZPJ1g==", + "dev": true, + "dependencies": { + "concat-stream": "^1.4.4", + "errno": "^0.1.1", + "fwd-stream": "^1.0.4", + "level-blobs": "^0.1.7", + "level-peek": "^1.0.6", + "level-sublevel": "^5.2.0", + "octal": "^1.0.0", + "once": "^1.3.0", + "xtend": "^2.2.0" + } + }, + "node_modules/level-fix-range": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/level-fix-range/-/level-fix-range-1.0.2.tgz", + "integrity": "sha512-9llaVn6uqBiSlBP+wKiIEoBa01FwEISFgHSZiyec2S0KpyLUkGR4afW/FCZ/X8y+QJvzS0u4PGOlZDdh1/1avQ==", + "dev": true + }, + "node_modules/level-hooks": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/level-hooks/-/level-hooks-4.5.0.tgz", + "integrity": "sha512-fxLNny/vL/G4PnkLhWsbHnEaRi+A/k8r5EH/M77npZwYL62RHi2fV0S824z3QdpAk6VTgisJwIRywzBHLK4ZVA==", + "dev": true, + "dependencies": { + "string-range": "~1.2" + } + }, + "node_modules/level-js": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/level-js/-/level-js-2.2.4.tgz", + "integrity": "sha512-lZtjt4ZwHE00UMC1vAb271p9qzg8vKlnDeXfIesH3zL0KxhHRDjClQLGLWhyR0nK4XARnd4wc/9eD1ffd4PshQ==", + "dev": true, + "dependencies": { + "abstract-leveldown": "~0.12.0", + "idb-wrapper": "^1.5.0", + "isbuffer": "~0.0.0", + "ltgt": "^2.1.2", + "typedarray-to-buffer": "~1.0.0", + "xtend": "~2.1.2" + } + }, + "node_modules/level-js/node_modules/xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==", + "dev": true, + "dependencies": { + "object-keys": "~0.4.0" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/level-peek": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/level-peek/-/level-peek-1.0.6.tgz", + "integrity": "sha512-TKEzH5TxROTjQxWMczt9sizVgnmJ4F3hotBI48xCTYvOKd/4gA/uY0XjKkhJFo6BMic8Tqjf6jFMLWeg3MAbqQ==", + "dev": true, + "dependencies": { + "level-fix-range": "~1.0.2" + } + }, + "node_modules/level-sublevel": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/level-sublevel/-/level-sublevel-5.2.3.tgz", + "integrity": "sha512-tO8jrFp+QZYrxx/Gnmjawuh1UBiifpvKNAcm4KCogesWr1Nm2+ckARitf+Oo7xg4OHqMW76eAqQ204BoIlscjA==", + "dev": true, + "dependencies": { + "level-fix-range": "2.0", + "level-hooks": ">=4.4.0 <5", + "string-range": "~1.2.1", + "xtend": "~2.0.4" + } + }, + "node_modules/level-sublevel/node_modules/level-fix-range": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/level-fix-range/-/level-fix-range-2.0.0.tgz", + "integrity": "sha512-WrLfGWgwWbYPrHsYzJau+5+te89dUbENBg3/lsxOs4p2tYOhCHjbgXxBAj4DFqp3k/XBwitcRXoCh8RoCogASA==", + "dev": true, + "dependencies": { + "clone": "~0.1.9" + } + }, + "node_modules/level-sublevel/node_modules/object-keys": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.2.0.tgz", + "integrity": "sha512-XODjdR2pBh/1qrjPcbSeSgEtKbYo7LqYNq64/TPuCf7j9SfDD3i21yatKoIy39yIWNvVM59iutfQQpCv1RfFzA==", + "deprecated": "Please update to the latest object-keys", + "dev": true, + "dependencies": { + "foreach": "~2.0.1", + "indexof": "~0.0.1", + "is": "~0.2.6" + } + }, + "node_modules/level-sublevel/node_modules/xtend": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.0.6.tgz", + "integrity": "sha512-fOZg4ECOlrMl+A6Msr7EIFcON1L26mb4NY5rurSkOex/TWhazOrg6eXD/B0XkuiYcYhQDWLXzQxLMVJ7LXwokg==", + "dev": true, + "dependencies": { + "is-object": "~0.1.2", + "object-keys": "~0.2.0" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/levelup": { + "version": "0.18.6", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-0.18.6.tgz", + "integrity": "sha512-uB0auyRqIVXx+hrpIUtol4VAPhLRcnxcOsd2i2m6rbFIDarO5dnrupLOStYYpEcu8ZT087Z9HEuYw1wjr6RL6Q==", + "dev": true, + "dependencies": { + "bl": "~0.8.1", + "deferred-leveldown": "~0.2.0", + "errno": "~0.1.1", + "prr": "~0.0.0", + "readable-stream": "~1.0.26", + "semver": "~2.3.1", + "xtend": "~3.0.0" + } + }, + "node_modules/levelup/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/levelup/node_modules/prr": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "integrity": "sha512-LmUECmrW7RVj6mDWKjTXfKug7TFGdiz9P18HMcO4RHL+RW7MCOGNvpj5j47Rnp6ne6r4fZ2VzyUWEpKbg+tsjQ==", + "dev": true + }, + "node_modules/levelup/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/levelup/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/levelup/node_modules/xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha512-sp/sT9OALMjRW1fKDlPeuSZlDQpkqReA0pyJukniWbTGoEKefHxhGJynE3PNhUMlcM8qWIjPwecwCw4LArS5Eg==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "peer": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true, + "peer": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==", + "dev": true + }, + "node_modules/octal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/octal/-/octal-1.0.0.tgz", + "integrity": "sha512-nnda7W8d+A3vEIY+UrDQzzboPf1vhs4JYVhff5CDkq9QNoZY7Xrxeo/htox37j9dZf7yNHevZzqtejWgy1vCqQ==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", + "integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==", + "dev": true, + "dependencies": { + "playwright-core": "1.43.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", + "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/process-es6": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/process-es6/-/process-es6-0.11.6.tgz", + "integrity": "sha512-GYBRQtL4v3wgigq10Pv58jmTbFXlIiTbSfgnNqZLY0ldUPqy1rRxDI5fCjoCpnM6TqmHQI8ydzTBXW86OYc0gA==", + "dev": true + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-node-builtins": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-builtins/-/rollup-plugin-node-builtins-2.1.2.tgz", + "integrity": "sha512-bxdnJw8jIivr2yEyt8IZSGqZkygIJOGAWypXvHXnwKAbUcN4Q/dGTx7K0oAJryC/m6aq6tKutltSeXtuogU6sw==", + "dev": true, + "dependencies": { + "browserify-fs": "^1.0.0", + "buffer-es6": "^4.9.2", + "crypto-browserify": "^3.11.0", + "process-es6": "^0.11.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-2.3.2.tgz", + "integrity": "sha512-abLdIKCosKfpnmhS52NCTjO4RiLspDfsn37prjzGrp9im5DPJOgh82Os92vtwGh6XdQryKI/7SREZnV+aqiXrA==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/string-range": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/string-range/-/string-range-1.2.2.tgz", + "integrity": "sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w==", + "dev": true + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.30.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz", + "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-1.0.4.tgz", + "integrity": "sha512-vjMKrfSoUDN8/Vnqitw2FmstOfuJ73G6CrSEKnf11A6RmasVxHqfeBcnTb6RsL4pTMuV5Zsv9IiHRphMZyckUw==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vite": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", + "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-static-copy": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.17.1.tgz", + "integrity": "sha512-9h3iaVs0bqnqZOM5YHJXGHqdC5VAVlTZ2ARYsuNpzhEJUHmFqXY7dAK4ZFpjEQ4WLFKcaN8yWbczr81n01U4sQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "fs-extra": "^11.1.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz", + "integrity": "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } +} diff --git a/projects/fusion-e2e/package.json b/projects/fusion-e2e/package.json index 9e96331ec..be9fff5e7 100644 --- a/projects/fusion-e2e/package.json +++ b/projects/fusion-e2e/package.json @@ -1,19 +1,44 @@ { - "name": "e2e", + "name": "e2e-fusion-ui", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "./shared-e2e-commands.js", + "module": "entities/index.ts", "scripts": { "pw:ci": "npx playwright test --reporter=html", "pw:local": "BASE_URL=http://localhost:6008/ npx playwright test", "pw:ui": "BASE_URL=https://fusion-storybook.ironsrc.mobi/branch_feature-isct-192-components-v4/ npx playwright test --ui", - "pw:ui:local": "BASE_URL=http://localhost:6008/ npx playwright test --ui" + "pw:ui:local": "BASE_URL=http://localhost:6008/ npx playwright test --ui", + "pw:build": "webpack build" }, - "keywords": [], + "exports": { + ".": { + "types": "./fusion-e2e/entities/index.d.ts", + "esm2020": "./fusion-pw-lib.js", + "es2020": "./fusion-pw-lib.js", + "es2015": "./fusion-pw-lib.js", + "node": "./fusion-pw-lib.js", + "default": "./fusion-pw-lib.js" + } + }, + "types": "./fusion-e2e/entities/index.d.ts", + "esm2020": "./fusion-pw-lib.js", + "es2020": "./fusion-pw-lib.js", + "es2015": "./fusion-pw-lib.js", + "node": "./fusion-pw-lib.js", + "default": "./fusion-pw-lib.js", "author": "", "license": "ISC", "devDependencies": { "@playwright/test": "^1.42.0", - "@types/node": "^20.11.22" + "@types/node": "^20.11.22", + "copy-webpack-plugin": "^12.0.2", + "rollup-plugin-node-builtins": "^2.1.2", + "ts-loader": "^9.5.1", + "tsconfig-paths-webpack-plugin": "^4.1.0", + "typescript": "^5.4.4", + "vite": "^4.4.5", + "vite-plugin-static-copy": "^0.17.0", + "webpack-cli": "^5.1.4" } } diff --git a/projects/fusion-e2e/playwright.config.ts b/projects/fusion-e2e/playwright.config.ts index 2739bb6a6..66075057a 100644 --- a/projects/fusion-e2e/playwright.config.ts +++ b/projects/fusion-e2e/playwright.config.ts @@ -10,14 +10,14 @@ import {defineConfig, devices} from '@playwright/test'; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './tests', - /* Run tests in files in parallel */ + testDir: './entities', + /* Run entities in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ + /* Opt out of parallel entities on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: 'html', diff --git a/projects/fusion-e2e/tests/components/alert/alert-component.ts b/projects/fusion-e2e/tests/components/alert/alert-component.ts deleted file mode 100644 index 33783eafa..000000000 --- a/projects/fusion-e2e/tests/components/alert/alert-component.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {Page} from '@playwright/test'; -import {AlertTestIdModifiers} from '@ironsource/fusion-ui/entities'; -import {getTestId, getTestIdSelector} from '../../global/utils'; - -export class AlertComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(getTestId(testId, AlertTestIdModifiers.WRAPPER)); - await this.page.waitForSelector(loadedPageSelector); - } - - getAlertText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, AlertTestIdModifiers.MESSAGE)).textContent(); - } - - getAlertTitle({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, AlertTestIdModifiers.TITLE)).textContent(); - } - - async clickOnActionButton({testId}: {testId: string}) { - await this.page.click(getTestId(testId, AlertTestIdModifiers.ACTION_BUTTON)); - } - - getActionButtonText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, AlertTestIdModifiers.ACTION_BUTTON)).textContent(); - } - - async closeAlert({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, AlertTestIdModifiers.CLOSE_BUTTON)).click(); - } - - async isAlertVisible({testId}: {testId: string}) { - const alertSelector = await this.page.getByTestId(getTestId(testId, AlertTestIdModifiers.WRAPPER)).count(); - return alertSelector > 0; - } - - getAlertIconType({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, AlertTestIdModifiers.WRAPPER)).getAttribute('class'); - } -} diff --git a/projects/fusion-e2e/tests/components/alert/alert-page.ts b/projects/fusion-e2e/tests/components/alert/alert-page.ts deleted file mode 100644 index 991bc53ff..000000000 --- a/projects/fusion-e2e/tests/components/alert/alert-page.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {AlertComponent} from './alert-component'; -import {alertStoryId, defaultTestId, loadedPageSelector} from './consts'; -import {Page} from '@playwright/test'; -import {ComponentBasePage} from '../base-page/component-base-page'; -import {ComponentProps} from '../base-page/types'; - -export class AlertPage extends ComponentBasePage { - readonly component: AlertComponent; - - constructor(page: Page) { - const alertProps: ComponentProps = { - page: page, - testId: defaultTestId, - componentId: alertStoryId, - loadedPageSelector: loadedPageSelector - }; - - super(alertProps); - this.component = new AlertComponent(page); - } - - getAlertText() { - return this.component.getAlertText({testId: this.testId}); - } - - getAlertTitle() { - return this.component.getAlertTitle({testId: this.testId}); - } - - async clickOnActionButton() { - await this.component.clickOnActionButton({testId: this.testId}); - } - - getActionButtonText() { - return this.component.getActionButtonText({testId: this.testId}); - } - - async closeAlert() { - await this.component.closeAlert({testId: this.testId}); - } - - isAlertVisible() { - return this.component.isAlertVisible({testId: this.testId}); - } - - async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); - } - - getAlertIconType() { - return this.component.getAlertIconType({testId: this.testId}); - } -} diff --git a/projects/fusion-e2e/tests/components/button/button-component.ts b/projects/fusion-e2e/tests/components/button/button-component.ts deleted file mode 100644 index 28a2f8eaa..000000000 --- a/projects/fusion-e2e/tests/components/button/button-component.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {Page} from '@playwright/test'; -import {getTestId, getTestIdSelector} from '../../global/utils'; -import {ButtonTestIdModifiers} from '@ironsource/fusion-ui/entities'; - -export class ButtonComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(getTestId(testId, ButtonTestIdModifiers.BUTTON)); - await this.page.waitForSelector(loadedPageSelector); - } - - async waitForToggleButtonComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(testId); - await this.page.waitForSelector(loadedPageSelector); - } - - async clickOnButton({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(getTestId(testId, ButtonTestIdModifiers.BUTTON)); - - await this.page.click(loadedPageSelector); - } - - async hoverOnButton({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(getTestId(testId, ButtonTestIdModifiers.BUTTON)); - - await this.page.hover(loadedPageSelector); - } - - async isButtonLoading({testId}: {testId: string}) { - const buttonClass = await this.page.getByTestId(testId).getAttribute('class'); - return buttonClass.includes('loading'); - } - - async getButtonText({testId}: {testId: string}) { - const buttonSelector = this.page.getByTestId(getTestId(testId, ButtonTestIdModifiers.CONTENT)); - return buttonSelector.textContent(); - } - - async getIconButtonText({testId}: {testId: string}) { - const textSelector = this.page.getByTestId(testId).last().locator('span'); - return textSelector.textContent(); - } - - isButtonDisabled({testId}: {testId: string}) { - return this.page.getByTestId(testId).isDisabled(); - } - - getToggleButtonFirstLabel({testId}: {testId: string}) { - return this.page.getByTestId(testId).textContent(); - } -} diff --git a/projects/fusion-e2e/tests/components/button/button-page.ts b/projects/fusion-e2e/tests/components/button/button-page.ts deleted file mode 100644 index dc0824c53..000000000 --- a/projects/fusion-e2e/tests/components/button/button-page.ts +++ /dev/null @@ -1,57 +0,0 @@ -import {Page} from '@playwright/test'; -import {ComponentBasePage} from '../base-page/component-base-page'; -import {ButtonComponent} from './button-component'; -import {ComponentProps} from '../base-page/types'; -import {buttonStoryId, defaultTestId, loadedPageSelector} from './consts'; - -export class ButtonPage extends ComponentBasePage { - readonly component: ButtonComponent; - - constructor(page: Page) { - const buttonProps: ComponentProps = { - page: page, - testId: defaultTestId, - componentId: buttonStoryId, - loadedPageSelector: loadedPageSelector - }; - - super(buttonProps); - this.component = new ButtonComponent(page); - } - - async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); - } - - async waitForToggleButtonComponent() { - await this.component.waitForToggleButtonComponent({testId: this.testId}); - } - - async clickOnButton() { - await this.component.clickOnButton({testId: this.testId}); - } - - async hoverOnButton() { - await this.component.hoverOnButton({testId: this.testId}); - } - - isButtonLoading() { - return this.component.isButtonLoading({testId: this.testId}); - } - - getButtonText() { - return this.component.getButtonText({testId: this.testId}); - } - - getIconButtonText() { - return this.component.getIconButtonText({testId: this.testId}); - } - - isButtonDisabled() { - return this.component.isButtonDisabled({testId: this.testId}); - } - - getToggleButtonFirstLabel() { - return this.component.getToggleButtonFirstLabel({testId: this.testId}); - } -} diff --git a/projects/fusion-e2e/tests/components/chart/chart-component.ts b/projects/fusion-e2e/tests/components/chart/chart-component.ts deleted file mode 100644 index 3a700e41f..000000000 --- a/projects/fusion-e2e/tests/components/chart/chart-component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {Page} from '@playwright/test'; -import {getTestIdSelector} from '../../global/utils'; - -export class ChartComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(testId); - await this.page.waitForSelector(loadedPageSelector); - } - - async getLabelText({testId}: {testId: string}) { - let label = this.page.getByTestId(testId); - return label.textContent(); - } -} diff --git a/projects/fusion-e2e/tests/components/chart/chart-page.ts b/projects/fusion-e2e/tests/components/chart/chart-page.ts deleted file mode 100644 index eb5adaef0..000000000 --- a/projects/fusion-e2e/tests/components/chart/chart-page.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Page} from '@playwright/test'; -import {chartStoryId, labelTestId, loadedPageSelector} from './consts'; -import {ComponentBasePage} from '../base-page/component-base-page'; -import {ComponentProps, GotoParams} from '../base-page/types'; -import {ChartComponent} from './chart-component'; -import {createStoryBookComponentPath} from '../../global/utils'; - -export class ChartPage extends ComponentBasePage { - readonly component: ChartComponent; - - constructor(page: Page) { - const chartProps: ComponentProps = { - page: page, - testId: labelTestId, - componentId: chartStoryId, - loadedPageSelector: loadedPageSelector - }; - - super(chartProps); - this.component = new ChartComponent(page); - } - - async goto(gotoParams: GotoParams = {}) { - const componentParams = { - testId: labelTestId, - ...gotoParams.additionalComponentParams - }; - - await this.page.goto(createStoryBookComponentPath(gotoParams.storyId || this.componentId, componentParams)); - - await this.page.waitForSelector(this.loadedPageSelector); - } - - async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); - } - - async getLabelText() { - return this.component.getLabelText({testId: this.testId}); - } -} diff --git a/projects/fusion-e2e/tests/components/chip-filter/chipFilter-component.ts b/projects/fusion-e2e/tests/components/chip-filter/chipFilter-component.ts deleted file mode 100644 index 4f94b7795..000000000 --- a/projects/fusion-e2e/tests/components/chip-filter/chipFilter-component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Page} from '@playwright/test'; -import {ChipFilterTestIdModifiers} from '@ironsource/fusion-ui/entities'; -import {getTestId, getTestIdSelector} from '../../global/utils'; - -export class ChipFilterComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(testId); - await this.page.waitForSelector(loadedPageSelector); - } - - getChipFilterLabel({testId}: {testId: string}) { - const locator = this.page.getByTestId(getTestId(testId, ChipFilterTestIdModifiers.CHIP_FILTER)).locator('.fu-chip-label'); - return locator.textContent(); - } - - click({testId}: {testId: string}) { - const locator = this.page.getByTestId(getTestId(testId, ChipFilterTestIdModifiers.CHIP_FILTER)).locator('.fu-chip-label'); - return locator.click(); - } -} diff --git a/projects/fusion-e2e/tests/components/dialog/dialog-component.ts b/projects/fusion-e2e/tests/components/dialog/dialog-component.ts deleted file mode 100644 index 4a2e7498a..000000000 --- a/projects/fusion-e2e/tests/components/dialog/dialog-component.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {Page} from '@playwright/test'; -import {DialogTestIdModifiers} from '@ironsource/fusion-ui/entities'; -import {getTestId, getTestIdSelector} from '../../global/utils'; - -export class DialogComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(getTestId(testId, DialogTestIdModifiers.WRAPPER)); - await this.page.waitForSelector(loadedPageSelector); - } - - getDialogTitle({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, DialogTestIdModifiers.HEADER)).textContent(); - } - - getDialogText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, DialogTestIdModifiers.MODAL_CONTENT)).textContent(); - } - - async openDialog({testId}: {testId: string}) { - const selector = getTestId(testId, DialogTestIdModifiers.WRAPPER); - await this.page.waitForSelector(getTestIdSelector(selector)); - const dialogButton = this.page.getByTestId(selector); - await dialogButton.click(); - } - - async closeDialog({testId}: {testId: string}) { - const closeButton = await this.page.getByTestId(getTestId(testId, DialogTestIdModifiers.ACTION_CLOSE)); - await closeButton.click(); - } - - async clickOnPrimaryButton({testId}: {testId: string}) { - const primaryButton = this.page.getByTestId(getTestId(testId, DialogTestIdModifiers.SAVE_BUTTON)); - await primaryButton.click(); - } - - async clickOnDefaultButton({testId}: {testId: string}) { - const defaultButton = this.page.getByTestId(getTestId(testId, DialogTestIdModifiers.CANCEL_BUTTON)); - await defaultButton.click(); - } - - async clickOnDeleteButton({testId}: {testId: string}) { - const deleteButton = await this.page - .getByTestId(getTestId(testId, DialogTestIdModifiers.ACTION_BUTTONS_WRAPPER)) - .locator('.danger'); - - await deleteButton.click(); - } - - getDialogSubtitle({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, DialogTestIdModifiers.HEADER_SECONDARY)).locator('.subtitle').textContent(); - } - - async isDialogVisible({testId}: {testId: string}) { - await this.page.waitForTimeout(1000); - const dialog = this.page.getByTestId(getTestId(testId, DialogTestIdModifiers.MODAL_WRAPPER)); - return dialog.isVisible(); - } -} diff --git a/projects/fusion-e2e/tests/components/dropdown/base-dropdown.ts b/projects/fusion-e2e/tests/components/dropdown/base-dropdown.ts deleted file mode 100644 index 06dbb467e..000000000 --- a/projects/fusion-e2e/tests/components/dropdown/base-dropdown.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {Page} from '@playwright/test'; -import {getTestId} from '../../global/utils'; -import {SelectionByIndex, SelectionByName, SelectMultiple, SelectMultipleByName} from './types'; -import {DropdownTestIdModifiers} from '@ironsource/fusion-ui/entities'; - -export class BaseDropdownComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.WRAPPER)); - } - - async selectDropdownOptionByIndex({testId, index}: SelectionByIndex) { - await this.openDropdownComponent({testId: testId}); - await this.page - .getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)) - .locator('fusion-dropdown-options-list > li') - .nth(index) - .click(); - } - - async getDropdownButtonContent(testId: string) { - await this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.BUTTON_CONTENT)).textContent(); - } - - async openDropdownComponent({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.TRIGGER)).click({ - position: { - x: 15, - y: 15 - } - }); - } - - async selectDropdownOptionByName({testId, name, shouldOpen = true}: SelectionByName) { - if (shouldOpen) await this.openDropdownComponent({testId: testId}); - await this.page - .getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)) - .locator('fusion-dropdown-options-list > li', {hasText: name}) - .click(); - } - - async selectMultipleItemsByIndex({testId, itemsToSelect}: SelectMultiple) { - await this.openDropdownComponent({testId: testId}); - for (const i of itemsToSelect) { - await this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)).locator('ul > li').nth(i).click(); - } - } - - async selectMultipleItemsByName({testId, itemsToSelect}: SelectMultipleByName) { - await this.openDropdownComponent({testId: testId}); - for (const name of itemsToSelect) { - await this.page - .getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)) - .locator('ul > li', {hasText: name}) - .first() - .click(); - } - } - - async clickOnApply({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.ACTION_APPLY)).click(); - } - - async clickOnCancel({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.ACTION_CANCEL)).click(); - } -} diff --git a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown-component.ts b/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown-component.ts deleted file mode 100644 index fdd35615b..000000000 --- a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown-component.ts +++ /dev/null @@ -1,96 +0,0 @@ -import {Page} from '@playwright/test'; - -import {getTestId} from '../../../global/utils'; -import {BaseDropdownComponent} from '../base-dropdown'; -import {FieldLabelComponent} from '../../fieldLabel/field-label-component'; -import {FieldHelpTextComponent} from '../../fieldHelpText/field-help-text-component'; -import {HasHelpTextTypeParams} from '../../fieldHelpText/types'; -import {DropdownTestIdModifiers, FieldHelpTextTestIdModifiers, InputTestIdModifiers} from '@ironsource/fusion-ui/entities'; - -type SearchItem = { - testId: string; - searchTerm: string; -}; - -export class DropdownComponent extends BaseDropdownComponent { - private readonly fieldLabelComponent; - private readonly fieldHelpTextComponent; - - constructor(page: Page) { - super(page); - this.fieldLabelComponent = new FieldLabelComponent(page); - this.fieldHelpTextComponent = new FieldHelpTextComponent(page); - } - - getDropdownTitle({testId}: {testId: string}) { - return this.fieldLabelComponent.getLabelText({testId: testId}); - } - - isMandatory({testId}: {testId: string}) { - return this.fieldLabelComponent.isMandatory({testId: testId}); - } - - getHelpIconText({testId}: {testId: string}) { - return this.fieldLabelComponent.getHelpIconText({testId: testId}); - } - - hasExtraText({testId}: {testId: string}) { - return this.fieldHelpTextComponent.hasExtraText({testId: testId}); - } - - getExtraText({testId}: {testId: string}) { - return this.fieldHelpTextComponent.getExtraText({testId: testId}); - } - - hasExtraTextIconType({testId, type}: HasHelpTextTypeParams) { - return this.fieldHelpTextComponent.hasExtraTextIconType({testId, type}); - } - - getSelectedLabel({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.BUTTON_CONTENT)).textContent(); - } - - async searchForItem({testId, searchTerm}: SearchItem) { - await this.openDropdownComponent({testId}); - await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)).last().fill(searchTerm); - return this.page - .getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)) - .locator('fusion-dropdown-options-list > li') - .first() - .textContent(); - } - - async isErrorText({testId}: {testId: string}) { - const errorTextLocator = await this.page.getByTestId(getTestId(testId, FieldHelpTextTestIdModifiers.TEXT)).count(); - return errorTextLocator > 0; - } - - async isDisabled({testId}: {testId: string}) { - const ddTriggerSelector = await this.page - .getByTestId(getTestId(testId, DropdownTestIdModifiers.TRIGGER)) - .locator('.button__container--disabled'); - - return ddTriggerSelector.isVisible(); - } - - async clearAllOptions({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.ACTION_CLEAR_ALL)).click(); - } - - isSelectAllChecked({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.SELECT_ALL)).locator('.fu-label-checkbox').isChecked(); - } - - async isSelectAllIndeterminate({testId}: {testId: string}) { - const locator = this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.SELECT_ALL)).locator('fusion-checkbox'); - return (await locator.getAttribute('ng-reflect-is-indeterminate')) === 'true'; - } - - async removeChipSelection({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.BUTTON_CLEAR)).click(); - } - - getDropdownOptions({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, DropdownTestIdModifiers.LIST_CONTAINER)).locator('.list').allTextContents(); - } -} diff --git a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown-page.ts b/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown-page.ts deleted file mode 100644 index 0812fd631..000000000 --- a/projects/fusion-e2e/tests/components/dropdown/dropdown-v4/dropdown-page.ts +++ /dev/null @@ -1,115 +0,0 @@ -import {Page} from '@playwright/test'; -import {dropdownDefaultStoryId, loadedPageSelector} from './consts'; -import {DropdownComponent} from './dropdown-component'; -import {ComponentBasePage} from '../../base-page/component-base-page'; -import {ComponentProps} from '../../base-page/types'; -import {testIdWithIndex} from '../consts'; - -export class DropdownPage extends ComponentBasePage { - readonly component: DropdownComponent; - - constructor(page: Page) { - const dropdownProps: ComponentProps = { - page: page, - testId: testIdWithIndex, - componentId: dropdownDefaultStoryId, - loadedPageSelector: loadedPageSelector - }; - - super(dropdownProps); - this.component = new DropdownComponent(page); - } - - async selectDropdownOptionByIndex(index: number) { - await this.component.selectDropdownOptionByIndex({ - testId: this.testId, - index - }); - } - - getSelectedLabel() { - return this.component.getSelectedLabel({testId: this.testId}); - } - - getDropdownTitle() { - return this.component.getDropdownTitle({testId: this.testId}); - } - - async selectDropdownOptionByName(name: string) { - await this.component.selectDropdownOptionByName({ - testId: this.testId, - name - }); - } - - async selectMultipleItems(itemsToSelect: number[], applyChanges = true) { - await this.component.selectMultipleItemsByIndex({ - testId: this.testId, - itemsToSelect - }); - if (applyChanges) { - await this.component.clickOnApply({testId: this.testId}); - } - } - - async clickOnCancel() { - await this.component.clickOnCancel({testId: this.testId}); - } - - searchForItem(searchTerm: string) { - return this.component.searchForItem({ - testId: this.testId, - searchTerm - }); - } - - async selectMultipleItemsByName(itemsToSelect: string[], applyChanges = true) { - await this.component.selectMultipleItemsByName({ - testId: this.testId, - itemsToSelect - }); - if (applyChanges) { - await this.component.clickOnApply({testId: this.testId}); - } - } - - isErrorState() { - return this.component.isErrorText({testId: this.testId}); - } - - isDisabled() { - return this.component.isDisabled({testId: this.testId}); - } - - async clearAllOptions() { - await this.component.clearAllOptions({testId: this.testId}); - } - - async openDropdownComponent() { - await this.component.openDropdownComponent({testId: this.testId}); - } - - async clickOnApply() { - await this.component.clickOnApply({testId: this.testId}); - } - - isSelectAllChecked() { - return this.component.isSelectAllChecked({testId: this.testId}); - } - - isSelectAllIndeterminate() { - return this.component.isSelectAllIndeterminate({testId: this.testId}); - } - - async removeChipSelection() { - await this.component.removeChipSelection({testId: this.testId}); - } - - async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); - } - - getDropdownOptions() { - return this.component.getDropdownOptions({testId: this.testId}); - } -} diff --git a/projects/fusion-e2e/tests/components/fieldHelpText/field-help-text-component.ts b/projects/fusion-e2e/tests/components/fieldHelpText/field-help-text-component.ts deleted file mode 100644 index 4fc391c8f..000000000 --- a/projects/fusion-e2e/tests/components/fieldHelpText/field-help-text-component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {getTestId} from '../../global/utils'; -import {HasHelpTextTypeParams} from './types'; -import {FieldHelpTextTestIdModifiers} from '@ironsource/fusion-ui/entities'; - -export class FieldHelpTextComponent { - readonly page; - - constructor(page) { - this.page = page; - } - - hasExtraText({testId}: {testId: string}) { - const byTestId = this.page.getByTestId(getTestId(testId, FieldHelpTextTestIdModifiers.TEXT)); - return byTestId.isVisible(); - } - - getExtraText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, FieldHelpTextTestIdModifiers.TEXT)).textContent(); - } - - async hasExtraTextIconType({testId, type}: HasHelpTextTypeParams) { - const extraTextIconTypeLocator = await this.page - .getByTestId(getTestId(testId, FieldHelpTextTestIdModifiers.CONTAINER)) - .locator(`.icon.icon-name--${type}`); - return (await extraTextIconTypeLocator.count()) !== 0; - } -} diff --git a/projects/fusion-e2e/tests/components/fieldLabel/field-label-component.ts b/projects/fusion-e2e/tests/components/fieldLabel/field-label-component.ts deleted file mode 100644 index 3ed05d019..000000000 --- a/projects/fusion-e2e/tests/components/fieldLabel/field-label-component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {getTestId} from '../../global/utils'; -import {Page} from '@playwright/test'; -import {FieldLabelTestIdModifiers} from '@ironsource/fusion-ui/entities'; - -export class FieldLabelComponent { - readonly page; - - constructor(page: Page) { - this.page = page; - } - - getLabelText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, FieldLabelTestIdModifiers.TEXT)).textContent(); - } - - isMandatory({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, FieldLabelTestIdModifiers.MANDATORY)).isVisible(); - } - - async getHelpIconText({testId}: {testId: string}) { - const helpIconSelector = await this.page.getByTestId(getTestId(testId, FieldLabelTestIdModifiers.TOOLTIP)); - await helpIconSelector.hover(); - return helpIconSelector.getAttribute('text'); - } -} diff --git a/projects/fusion-e2e/tests/components/inputs/base-input.ts b/projects/fusion-e2e/tests/components/inputs/base-input.ts deleted file mode 100644 index b153f8b15..000000000 --- a/projects/fusion-e2e/tests/components/inputs/base-input.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {Page} from '@playwright/test'; -import {getTestId} from '../../global/utils'; -import {InputParams} from './types'; -import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities'; - -export class BaseInputComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - getInputsFieldText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)).inputValue(); - } - - getPlaceholderText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)).getAttribute('placeholder'); - } - - getInputsType({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)).getAttribute('type'); - } - - async addInput({testId, text}: InputParams) { - await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)).type(text as string); - } - - async clearInput({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)).clear(); - } - - isDisabled({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)).isDisabled(); - } -} diff --git a/projects/fusion-e2e/tests/components/inputs/input-v4/inputs-component.ts b/projects/fusion-e2e/tests/components/inputs/input-v4/inputs-component.ts deleted file mode 100644 index 2c6b202f5..000000000 --- a/projects/fusion-e2e/tests/components/inputs/input-v4/inputs-component.ts +++ /dev/null @@ -1,130 +0,0 @@ -import {Page} from '@playwright/test'; -import {BaseInputComponent} from '../base-input'; -import {FieldLabelComponent} from '../../fieldLabel/field-label-component'; -import {FieldHelpTextComponent} from '../../fieldHelpText/field-help-text-component'; -import {HasHelpTextTypeParams} from '../../fieldHelpText/types'; -import {InputTestIdModifiers} from '@ironsource/fusion-ui/entities'; -import {getTestId, getTestIdSelector} from '../../../global/utils'; - -export class InputsComponent extends BaseInputComponent { - private readonly fieldLabelComponent: FieldLabelComponent; - private readonly fieldHelpTextComponent: FieldHelpTextComponent; - - constructor(page: Page) { - super(page); - this.fieldLabelComponent = new FieldLabelComponent(page); - this.fieldHelpTextComponent = new FieldHelpTextComponent(page); - } - - getInputsLabelText({testId}: {testId: string}) { - return this.fieldLabelComponent.getLabelText({testId: testId}); - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(getTestId(testId, InputTestIdModifiers.WRAPPER)); - - await this.page.waitForSelector(loadedPageSelector); - } - - isInputMandatory({testId}: {testId: string}) { - return this.fieldLabelComponent.isMandatory({testId: testId}); - } - - hasInputExtraText({testId}: {testId: string}) { - return this.fieldHelpTextComponent.hasExtraText({testId: testId}); - } - - getInputExtraText({testId}: {testId: string}) { - return this.fieldHelpTextComponent.getExtraText({testId: testId}); - } - - async clickOnApplyButton({testId}: {testId: string}) { - const applyButtonSelector = await this.page - .getByTestId(getTestId(testId, InputTestIdModifiers.WRAPPER)) - .locator('.icon.icon-name--check'); - await applyButtonSelector.click(); - } - - hasInlineErrorText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, InputTestIdModifiers.TOOLTIP)).isVisible(); - } - - async getInlineErrorText({testId}: {testId: string}) { - const inlineErrorSelector = await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.TOOLTIP)); - await inlineErrorSelector.hover(); - return inlineErrorSelector.getAttribute('text'); - } - - async clickOnShowPassword({testId}: {testId: string}) { - if (await this.isPasswordHidden({testId})) { - await this.clickOnPasswordIcon({testId}); - } - } - - async clickOnPasswordIcon({testId}: {testId: string}) { - await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.TOGGLE_PASSWORD)).click(); - } - - async clickOnHidePassword({testId}: {testId: string}) { - if (!(await this.isPasswordHidden({testId}))) { - await this.clickOnPasswordIcon({testId}); - } - } - - isPasswordHidden({testId}: {testId: string}) { - return this.page - .getByTestId(getTestId(testId, InputTestIdModifiers.TOGGLE_PASSWORD)) - .locator('.fu-show-password-button eye-slash') - .isVisible(); - } - - async clearInput({testId}: {testId: string}) { - const inputFieldSelector = this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)); - await inputFieldSelector.clear(); - } - - hasExtraTextIconType({testId, type}: HasHelpTextTypeParams) { - return this.fieldHelpTextComponent.hasExtraTextIconType({testId, type}); - } - - async isDisabled({testId}: {testId: string}) { - const inputFieldSelector = await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)); - - return inputFieldSelector.isDisabled(); - } - - hasApplyButton({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, InputTestIdModifiers.WRAPPER)).locator('.icon.icon-name--check').isVisible(); - } - - async getMaxLengthNumber({testId}: {testId: string}) { - // const fontCaptionText = await this.getFontCaptionText({testId}); - // return fontCaptionText[1]; - const inputFieldSelector = await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)); - return inputFieldSelector.getAttribute('maxlength'); - } - - async getActualNumberLength({testId}: {testId: string}) { - const inputFieldSelector = await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.FIELD)); - return inputFieldSelector.getAttribute('value').then(value => value.length); - // const fontCaptionText = await this.getFontCaptionText({testId}); - // return fontCaptionText[0]; - } - - async getFontCaptionText({testId}: {testId: string}) { - const fontCaptionText = await this.page - .getByTestId(getTestId(testId, InputTestIdModifiers.WRAPPER)) - .locator('.font-caption') - .textContent(); - return fontCaptionText.split('/').map(str => parseInt(str.trim(), 10)); - } - - getHelpIconText({testId}: {testId: string}) { - return this.fieldLabelComponent.getHelpIconText({testId: testId}); - } - - async isValidationAppear({testId}: {testId: string}) { - const fieldClasses = await this.page.getByTestId(getTestId(testId, InputTestIdModifiers.WRAPPER)).getAttribute('class'); - return fieldClasses.includes('variant-error'); - } -} diff --git a/projects/fusion-e2e/tests/components/inputs/input-v4/inputs-page.ts b/projects/fusion-e2e/tests/components/inputs/input-v4/inputs-page.ts deleted file mode 100644 index 91e00555e..000000000 --- a/projects/fusion-e2e/tests/components/inputs/input-v4/inputs-page.ts +++ /dev/null @@ -1,115 +0,0 @@ -import {Page} from '@playwright/test'; -import {InputsComponent} from './inputs-component'; -import {defaultTestId, inputsStoryId, loadedPageSelector} from './consts'; -import {ComponentBasePage} from '../../base-page/component-base-page'; -import {ComponentProps} from '../../base-page/types'; - -export class InputsPage extends ComponentBasePage { - readonly component: InputsComponent; - - constructor(page: Page) { - const inputsProps: ComponentProps = { - page: page, - testId: defaultTestId, - componentId: inputsStoryId, - loadedPageSelector: loadedPageSelector - }; - - super(inputsProps); - this.component = new InputsComponent(page); - } - - getInputsFieldText() { - return this.component.getInputsFieldText({testId: this.testId}); - } - - getInputsLabelText() { - return this.component.getInputsLabelText({testId: this.testId}); - } - - addInput({textInput}: {textInput: string}) { - return this.component.addInput({ - testId: this.testId, - text: textInput - }); - } - - isInputMandatory() { - return this.component.isInputMandatory({testId: this.testId}); - } - - getPlaceholderText() { - return this.component.getPlaceholderText({testId: this.testId}); - } - - hasInputExtraText() { - return this.component.hasInputExtraText({testId: this.testId}); - } - - getInputExtraText() { - return this.component.getInputExtraText({testId: this.testId}); - } - - getInputsType() { - return this.component.getInputsType({testId: this.testId}); - } - - hasInlineErrorText() { - return this.component.hasInlineErrorText({testId: this.testId}); - } - - getInlineErrorText() { - return this.component.getInlineErrorText({testId: this.testId}); - } - - async clickOnApplyButton() { - await this.component.clickOnApplyButton({testId: this.testId}); - } - - hasApplyButton() { - return this.component.hasApplyButton({testId: this.testId}); - } - - hasExtraTextIconType({type}: {type: string}) { - return this.component.hasExtraTextIconType({ - testId: this.testId, - type: type - }); - } - - isDisabled() { - return this.component.isDisabled({testId: this.testId}); - } - - getMaxLengthNumber() { - return this.component.getMaxLengthNumber({testId: this.testId}); - } - - getActualNumberLength() { - return this.component.getActualNumberLength({testId: this.testId}); - } - - getHelpIconText() { - return this.component.getHelpIconText({testId: this.testId}); - } - - async clickOnShowPassword() { - await this.component.clickOnShowPassword({testId: this.testId}); - } - - async clickOnHidePassword() { - await this.component.clickOnHidePassword({testId: this.testId}); - } - - isPasswordHidden() { - return this.component.isPasswordHidden({testId: this.testId}); - } - - async waitForComponent() { - return this.component.waitForComponent({testId: this.testId}); - } - - async clearInput() { - await this.component.clearInput({testId: this.testId}); - } -} diff --git a/projects/fusion-e2e/tests/components/tabs/tabs-component.ts b/projects/fusion-e2e/tests/components/tabs/tabs-component.ts deleted file mode 100644 index 4fe60ead3..000000000 --- a/projects/fusion-e2e/tests/components/tabs/tabs-component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {Page} from '@playwright/test'; -import {getTestId, getTestIdSelector} from '../../global/utils'; -import {TabsSelectionParams} from './types'; -import {TabsTestIdModifiers} from '@ironsource/fusion-ui/entities'; -import {defaultTestId} from './consts'; - -export class TabsComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(testId); - await this.page.waitForSelector(loadedPageSelector); - } - - getSelectedTabText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, TabsTestIdModifiers.WRAPPER)).locator('.tab-item--active').textContent(); - } - - async selectTab({testId, tabName}: TabsSelectionParams) { - const tabIndex = await this.getTabIndex({testId, tabName}); - await this.page.getByTestId(getTestId(testId, `${TabsTestIdModifiers.TAB}-${tabIndex + 1}`)).click(); - } - - private async getTabIndex({testId, tabName}: TabsSelectionParams) { - const tabs = await this.page.getByTestId(getTestId(testId, TabsTestIdModifiers.WRAPPER)).locator('.tab-item').allTextContents(); - return tabs.indexOf(tabName); - } - - async isTabDisabled() { - const disabledTestId = getTestId(defaultTestId, TabsTestIdModifiers.TAB_DISABLED); - await this.waitForComponent({testId: disabledTestId}); - return this.page.getByTestId(disabledTestId).isDisabled(); - } -} diff --git a/projects/fusion-e2e/tests/components/tabs/tabs-page.ts b/projects/fusion-e2e/tests/components/tabs/tabs-page.ts deleted file mode 100644 index e38f1d311..000000000 --- a/projects/fusion-e2e/tests/components/tabs/tabs-page.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {Page} from '@playwright/test'; -import {tabsStoryId, loadedPageSelector, wrapperTestId} from './consts'; -import {ComponentBasePage} from '../base-page/component-base-page'; -import {ComponentProps} from '../base-page/types'; -import {TabsComponent} from './tabs-component'; - -export class TabsPage extends ComponentBasePage { - readonly component: TabsComponent; - - constructor(page: Page) { - const tabsProps: ComponentProps = { - page: page, - testId: wrapperTestId, - componentId: tabsStoryId, - loadedPageSelector: loadedPageSelector - }; - - super(tabsProps); - this.component = new TabsComponent(page); - } - - async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); - } - - getSelectedTabText() { - return this.component.getSelectedTabText({testId: this.testId}); - } - - async selectTab({tabName}: {tabName: string}) { - await this.component.selectTab({testId: this.testId, tabName}); - } - - isTabDisabled() { - return this.component.isTabDisabled(); - } -} diff --git a/projects/fusion-e2e/tests/components/tooltip/tooltip-component.ts b/projects/fusion-e2e/tests/components/tooltip/tooltip-component.ts deleted file mode 100644 index 9ac008a26..000000000 --- a/projects/fusion-e2e/tests/components/tooltip/tooltip-component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Page} from '@playwright/test'; -import {TooltipTestIdModifiers} from '@ironsource/fusion-ui/entities'; -import {getTestId, getTestIdSelector} from '../../global/utils'; - -export class TooltipComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(getTestId(testId, TooltipTestIdModifiers.TRIGGER)); - - await this.page.waitForSelector(loadedPageSelector); - } - - getTooltipText({testId}: {testId: string}) { - const locator = this.page.getByTestId(getTestId(testId, TooltipTestIdModifiers.TRIGGER)).last().locator('span'); - return locator.textContent(); - } - - getTooltipHeaderText({testId}: {testId: string}) { - return this.page.getByTestId(getTestId(testId, TooltipTestIdModifiers.TRIGGER)).last().getAttribute('header'); - } -} diff --git a/projects/fusion-e2e/tests/components/trendindicator/trend-indicator-page.ts b/projects/fusion-e2e/tests/components/trendindicator/trend-indicator-page.ts deleted file mode 100644 index ff7871b56..000000000 --- a/projects/fusion-e2e/tests/components/trendindicator/trend-indicator-page.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {TrendIndicatorComponent} from './trendindicator-component'; -import {trendIndicatorStoryId, defaultTestId, loadedPageSelector} from './consts'; -import {Page} from '@playwright/test'; -import {ComponentBasePage} from '../base-page/component-base-page'; -import {ComponentProps} from '../base-page/types'; - -export class TrendIndicatorPage extends ComponentBasePage { - readonly component: TrendIndicatorComponent; - - constructor(page: Page) { - const alertProps: ComponentProps = { - page: page, - testId: defaultTestId, - componentId: trendIndicatorStoryId, - loadedPageSelector: loadedPageSelector - }; - - super(alertProps); - this.component = new TrendIndicatorComponent(page); - } - - async waitForComponent() { - await this.component.waitForComponent({testId: this.testId}); - } - - async getTrendIndicatorValue() { - return this.component.getTrendIndicatorValue({testId: this.testId}); - } -} diff --git a/projects/fusion-e2e/tests/components/trendindicator/trendindicator-component.ts b/projects/fusion-e2e/tests/components/trendindicator/trendindicator-component.ts deleted file mode 100644 index cc555a7f9..000000000 --- a/projects/fusion-e2e/tests/components/trendindicator/trendindicator-component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {Page} from '@playwright/test'; -import {getTestId, getTestIdSelector} from '../../global/utils'; -import {TrendIndicatorTestIdModifiers} from '@ironsource/fusion-ui/entities'; - -export class TrendIndicatorComponent { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async waitForComponent({testId}: {testId: string}) { - const loadedPageSelector = getTestIdSelector(testId); - await this.page.waitForSelector(loadedPageSelector); - } - - getTrendIndicatorValue({testId}: {testId: string}) { - const locator = this.page.getByTestId(getTestId(testId, TrendIndicatorTestIdModifiers.VALUE)); - return locator.textContent(); - } -} diff --git a/projects/fusion-e2e/tsconfig.json b/projects/fusion-e2e/tsconfig.json new file mode 100644 index 000000000..9a67aa1bb --- /dev/null +++ b/projects/fusion-e2e/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "noImplicitAny": false, + "module": "es6", + "target": "es5", + "allowJs": true, + "baseUrl": ".", + "moduleResolution": "node", + "declaration": true, + "paths": { + "@ironsource/fusion-ui/*": ["../fusion-ui/*"], + "@ironsource/fusion-ui": ["../fusion-ui"] + }, + "types": ["node"] + }, + "exclude": ["node_modules"], + "include": ["entities/**/*"] +} diff --git a/projects/fusion-e2e/webpack.config.ts b/projects/fusion-e2e/webpack.config.ts new file mode 100644 index 000000000..3692fcb6c --- /dev/null +++ b/projects/fusion-e2e/webpack.config.ts @@ -0,0 +1,41 @@ +const path = require('path'); +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); + +module.exports = { + mode: 'production', + entry: './entities/index.ts', + target: 'node', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'fusion-pw-lib.js', + library: { + name: 'fusionPWLib', + type: 'umd' + } + }, + externals: ['playwright', '@playwright/test'], + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/ + } + ] + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + plugins: [new TsconfigPathsPlugin({configFile: './tsconfig.json'})] + }, + plugins: [ + new CopyPlugin({ + patterns: [ + { + from: 'package.json', + to: '.' + } + ] + }) + ] +}; diff --git a/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.html b/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.html index 4e84b54e1..88eb6a547 100644 --- a/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.html +++ b/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.html @@ -1,5 +1,6 @@ -
-
+
- -
+
{{ datasetLabel.label }}
diff --git a/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.ts b/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.ts index 7b8ba6d89..0c109bb70 100644 --- a/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.ts +++ b/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.component.ts @@ -9,6 +9,8 @@ import {TooltipDirective} from '@ironsource/fusion-ui/components/tooltip/v4'; import {ColorsService} from '@ironsource/fusion-ui/services/colors'; import {CheckboxComponent} from '@ironsource/fusion-ui/components/checkbox/v4'; import {isNullOrUndefined} from '@ironsource/fusion-ui/utils'; +import {ChartLabelTestIdModifiers} from '@ironsource/fusion-ui/entities'; +import {TestIdsService} from '@ironsource/fusion-ui/services/test-ids'; @Component({ selector: 'fusion-chart-labels', @@ -80,4 +82,7 @@ export class ChartLabelsV4Component implements OnInit, OnDestroy { $event.stopPropagation(); this.labelClick$.next(chartLabel); } + + protected readonly ChartLabelTestIdModifiers = ChartLabelTestIdModifiers; + protected readonly getTestId = TestIdsService.getTestId; } diff --git a/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.stories.mock.ts b/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.stories.mock.ts new file mode 100644 index 000000000..3f8bcc6a6 --- /dev/null +++ b/projects/fusion-ui/components/chart-labels/v4/chart-labels-v4.stories.mock.ts @@ -0,0 +1,26 @@ +import {COLORS_PALETTE} from '@ironsource/fusion-ui/services/colors/colors-palette'; +import {ChartLabel} from '@ironsource/fusion-ui/components/chart/common/base'; + +export const CHART_COLORS = COLORS_PALETTE['style_v4']; +export const CART_LABELS_MOCK: ChartLabel[] = [ + { + color: CHART_COLORS[0], + label: 'Label 1' + }, + { + color: CHART_COLORS[1], + label: 'Label 2' + } +]; +export const CART_LABELS_ICONS_MOCK: ChartLabel[] = [ + { + color: CHART_COLORS[0], + label: 'Label 1', + icon: 'v4/branded/android' + }, + { + color: CHART_COLORS[1], + label: 'Label 2', + icon: 'v4/branded/ios' + } +]; diff --git a/projects/fusion-ui/components/chart/common/base/chart.base.component.ts b/projects/fusion-ui/components/chart/common/base/chart.base.component.ts index 38417a9b2..c8c69f083 100644 --- a/projects/fusion-ui/components/chart/common/base/chart.base.component.ts +++ b/projects/fusion-ui/components/chart/common/base/chart.base.component.ts @@ -58,6 +58,8 @@ export abstract class ChartBaseComponent implements OnInit, OnDestroy, OnChanges /** @internal */ @Input() type: ChartType; + @Input() testId!: string; + @Input() set data(value: ChartData | FusionChartPieData) { this._data = {...value}; } diff --git a/projects/fusion-ui/components/chart/v4/chart-v4.component.html b/projects/fusion-ui/components/chart/v4/chart-v4.component.html index 549825936..f35728e6b 100644 --- a/projects/fusion-ui/components/chart/v4/chart-v4.component.html +++ b/projects/fusion-ui/components/chart/v4/chart-v4.component.html @@ -1,3 +1,3 @@ -
+
-
\ No newline at end of file +
diff --git a/projects/fusion-ui/components/chart/v4/stories/chart-v4-bar.component.stories.ts b/projects/fusion-ui/components/chart/v4/stories/chart-v4-bar.component.stories.ts index 18cc21458..32486e710 100644 --- a/projects/fusion-ui/components/chart/v4/stories/chart-v4-bar.component.stories.ts +++ b/projects/fusion-ui/components/chart/v4/stories/chart-v4-bar.component.stories.ts @@ -35,7 +35,8 @@ export default { }, args: { data: {...CHART_BAR_DATA_MOCK}, - type: ChartType.Bar + type: ChartType.Bar, + testId: 'chart-test-id' } } as Meta; @@ -56,10 +57,11 @@ export const Stack: Story = { render: args => ({ props: { data: {...CHART_BAR_GROUPED_DATA_MOCK}, - type: ChartType.StackedBar + type: ChartType.StackedBar, + testId: args.testId }, template: ` - + ` }) }; diff --git a/projects/fusion-ui/components/chart/v4/stories/chart-v4-story-wrapper.component.ts b/projects/fusion-ui/components/chart/v4/stories/chart-v4-story-wrapper.component.ts index 44f175a3b..321e4173b 100644 --- a/projects/fusion-ui/components/chart/v4/stories/chart-v4-story-wrapper.component.ts +++ b/projects/fusion-ui/components/chart/v4/stories/chart-v4-story-wrapper.component.ts @@ -5,6 +5,7 @@ import {BehaviorSubject} from 'rxjs'; import {ChartComponent} from '@ironsource/fusion-ui/components/chart/v4'; import {ChartLabelsComponent} from '@ironsource/fusion-ui/components/chart-labels/v4'; import {ChartData, ChartDataset, ChartLabel, ChartType} from '@ironsource/fusion-ui/components/chart/common/base'; +import {getTestId, ChartLabelTestIdModifiers} from '@ironsource/fusion-ui/entities'; @Component({ selector: 'fusion-chart-wrapper', @@ -18,6 +19,7 @@ import {ChartData, ChartDataset, ChartLabel, ChartType} from '@ironsource/fusion [data]="data" [type]="type" [options]="options" + [testId]="testId" (afterDatasetInit)="onChartInit($event)" >
@@ -26,6 +28,7 @@ import {ChartData, ChartDataset, ChartLabel, ChartType} from '@ironsource/fusion [labels]="chartDataLabels$ | async" (labelHover)="labelHovered($event)" (labelClick)="labelClicked($event)" + [testId]="getTestId(testId, ChartLabelTestIdModifiers.LABEL)" >
@@ -46,6 +49,7 @@ export class ChartV4WrapperComponent { private _data: ChartData; @Input() type: ChartType; @Input() options: any; + @Input() testId: string; @Input() labelsClickable = false; @Input() labelOther = false; @@ -59,16 +63,14 @@ export class ChartV4WrapperComponent { const legends = chartDatasets[0]?.legends; const values = chartDatasets[0]?.data; const colors = chartDatasets[0]?.backgroundColor; - chartDataLabels = legends - .map((legend: string, idx: number) => { - const dataLabel: ChartLabel = { - id: idx, - label: legend, - color: colors[idx] - }; - return dataLabel; - }) - .reverse(); + chartDataLabels = legends.map((legend: string, idx: number) => { + const dataLabel: ChartLabel = { + id: idx, + label: legend, + color: colors[idx] + }; + return dataLabel; + }); } else { chartDataLabels = chartDatasets.map((dataSet, idx) => { const dataLabel: ChartLabel = { @@ -100,8 +102,10 @@ export class ChartV4WrapperComponent { labelHovered(label: ChartLabel): void { this.fusionChart?.highlightDataset(label); } - labelClicked(label: ChartLabel): void { this.fusionChart?.toggleDataset(label, true); } + + protected readonly getTestId = getTestId; + protected readonly ChartLabelTestIdModifiers = ChartLabelTestIdModifiers; } diff --git a/projects/fusion-ui/entities/test-ids-modifiers.ts b/projects/fusion-ui/entities/test-ids-modifiers.ts index 9c2558810..18e658576 100644 --- a/projects/fusion-ui/entities/test-ids-modifiers.ts +++ b/projects/fusion-ui/entities/test-ids-modifiers.ts @@ -1,3 +1,5 @@ +export const getTestId = (testId: string, testIdModifier: string) => `${testId}--${testIdModifier}`; + export enum ChipFilterTestIdModifiers { CHIP_FILTER = 'chf', RESET_BUTTON = 'chf-reset-button'