mirror of https://github.com/Squidex/squidex.git
Browse Source
* Improve e2e tests * Fix stories. * Page objects. * Save file. * Rename screenshots. * Fix fixture. * Fix dropdowns. * Rename * assets testspull/1127/head
committed by
GitHub
60 changed files with 1375 additions and 436 deletions
@ -0,0 +1,6 @@ |
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="20%" height="30%"> |
|||
<path d="M30,1h40l29,29v40l-29,29h-40l-29-29v-40z" stroke="#000" fill="none"/> |
|||
<path d="M31,3h38l28,28v38l-28,28h-38l-28-28v-38z" fill="#a23"/> |
|||
<text x="50" y="68" font-size="48" fill="#FFF" text-anchor="middle"><![CDATA[410]]></text> |
|||
<use id="#other"/> |
|||
</svg> |
|||
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
@ -0,0 +1,80 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { readFile } from 'fs/promises'; |
|||
import path from 'path'; |
|||
import { AssetsPage } from '../pages'; |
|||
import { getRandomId } from '../utils'; |
|||
import { expect, test } from './_fixture'; |
|||
|
|||
test.beforeEach(async ({ appName, assetsPage }) => { |
|||
await assetsPage.goto(appName); |
|||
}); |
|||
|
|||
test('should upload asset', async ({ assetsPage }) => { |
|||
const assetName = await uploadRandomAsset(assetsPage); |
|||
const assetCard = await assetsPage.getAssetCard(assetName); |
|||
|
|||
expect(assetCard.root).toBeVisible(); |
|||
}); |
|||
|
|||
test('should delete asset', async ({ assetsPage }) => { |
|||
const assetName = await uploadRandomAsset(assetsPage); |
|||
const assetCard = await assetsPage.getAssetCard(assetName); |
|||
await assetCard.delete(); |
|||
|
|||
expect(assetCard.root).not.toBeVisible(); |
|||
}); |
|||
|
|||
test('should not delete asset if cancelled', async ({ assetsPage }) => { |
|||
const assetName = await uploadRandomAsset(assetsPage); |
|||
const assetCard = await assetsPage.getAssetCard(assetName); |
|||
|
|||
await assetCard.delete(true); |
|||
|
|||
expect(assetCard.root).toBeVisible(); |
|||
}); |
|||
|
|||
test('should edit asset name', async ({ assetsPage }) => { |
|||
const assetName = await uploadRandomAsset(assetsPage); |
|||
const assetCard = await assetsPage.getAssetCard(assetName); |
|||
|
|||
const newName = `file-${getRandomId()}`; |
|||
const assetDialog = await assetCard.edit(); |
|||
await assetDialog.enterName(newName); |
|||
await assetDialog.save(); |
|||
|
|||
const newCard = await assetsPage.getAssetCard(newName); |
|||
|
|||
expect(newCard.root).toBeVisible(); |
|||
}); |
|||
|
|||
test('should edit asset metadata', async ({ assetsPage }) => { |
|||
const assetName = await uploadRandomAsset(assetsPage); |
|||
const assetCard = await assetsPage.getAssetCard(assetName); |
|||
|
|||
const w = '42'; |
|||
const h = '13'; |
|||
|
|||
const assetDialog = await assetCard.edit(); |
|||
await assetDialog.enterMetadata('pixelWidth', w); |
|||
await assetDialog.enterMetadata('pixelHeight', h); |
|||
await assetDialog.save(); |
|||
|
|||
const newCard = await assetsPage.getAssetCard(`${w}x${h}px`); |
|||
|
|||
expect(newCard.root).toBeVisible(); |
|||
}); |
|||
|
|||
async function uploadRandomAsset(assetsPage: AssetsPage) { |
|||
const fileName = `file-${getRandomId()}`; |
|||
const fileBuffer = await readFile(path.join(__dirname, '../../assets/logo-squared.png')); |
|||
|
|||
await assetsPage.uploadFile({ name: fileName, mimeType: 'image/png', buffer: fileBuffer }); |
|||
|
|||
return fileName; |
|||
} |
|||
@ -1,90 +1,80 @@ |
|||
import { expect, Page } from '@playwright/test'; |
|||
import { escapeRegex, getRandomId } from '../utils'; |
|||
import { expect } from '@playwright/test'; |
|||
import { RulePage, RulesPage } from '../pages'; |
|||
import { getRandomId } from '../utils'; |
|||
import { test } from './_fixture'; |
|||
|
|||
// We have no easy way to identity rules. Therefore run them sequentially.
|
|||
test.describe.configure({ mode: 'serial' }); |
|||
|
|||
test.beforeEach(async ({ page, appName }) => { |
|||
await page.goto(`/app/${appName}/rules`); |
|||
test.beforeEach(async ({ appName, rulesPage }) => { |
|||
await rulesPage.goto(appName); |
|||
}); |
|||
|
|||
test('create rule', async ({ page }) => { |
|||
const ruleName = await createRandomRule(page); |
|||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
|||
test('create rule', async ({ rulesPage, rulePage }) => { |
|||
const ruleName = await createRandomRule(rulesPage, rulePage); |
|||
const ruleCard = await rulesPage.getRule(ruleName); |
|||
|
|||
await expect(ruleCard).toBeVisible(); |
|||
await expect(ruleCard.root).toBeVisible(); |
|||
}); |
|||
|
|||
test('delete rule', async ({ dropdown, page }) => { |
|||
const ruleName = await createRandomRule(page); |
|||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
|||
test('delete rule', async ({ rulesPage, rulePage }) => { |
|||
const ruleName = await createRandomRule(rulesPage, rulePage); |
|||
const ruleCard = await rulesPage.getRule(ruleName); |
|||
|
|||
await ruleCard.getByLabel('Options').click(); |
|||
const dropdown = await ruleCard.openOptionsDropdown(); |
|||
await dropdown.delete(); |
|||
|
|||
await expect(ruleCard).not.toBeVisible(); |
|||
await expect(ruleCard.root).not.toBeVisible(); |
|||
}); |
|||
|
|||
test('disable rule', async ({ dropdown, page }) => { |
|||
const ruleName = await createRandomRule(page); |
|||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
|||
test('disable rule', async ({ rulePage, rulesPage }) => { |
|||
const ruleName = await createRandomRule(rulesPage, rulePage); |
|||
const ruleCard = await rulesPage.getRule(ruleName); |
|||
|
|||
await ruleCard.getByLabel('Options').click(); |
|||
const dropdown = await ruleCard.openOptionsDropdown(); |
|||
await dropdown.action('Disable'); |
|||
|
|||
await expect(ruleCard.locator('sqx-toggle .toggle-container')).toHaveAttribute('data-state', 'unchecked'); |
|||
await expect(ruleCard.root.locator('sqx-toggle .toggle-container')).toHaveAttribute('data-state', 'unchecked'); |
|||
}); |
|||
|
|||
test('enable rule', async ({ dropdown, page }) => { |
|||
const ruleName = await createRandomRule(page); |
|||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
|||
test('enable rule', async ({ rulePage, rulesPage }) => { |
|||
const ruleName = await createRandomRule(rulesPage, rulePage); |
|||
const ruleCard = await rulesPage.getRule(ruleName); |
|||
|
|||
await ruleCard.getByLabel('Options').click(); |
|||
await dropdown.action('Disable'); |
|||
const dropdown1 = await ruleCard.openOptionsDropdown(); |
|||
await dropdown1.action('Disable'); |
|||
|
|||
await expect(ruleCard.locator('sqx-toggle .toggle-container')).toHaveAttribute('data-state', 'unchecked'); |
|||
await expect(ruleCard.root.locator('sqx-toggle .toggle-container')).toHaveAttribute('data-state', 'unchecked'); |
|||
|
|||
await ruleCard.getByLabel('Options').click(); |
|||
await dropdown.action('Enable'); |
|||
const dropdown2 = await ruleCard.openOptionsDropdown(); |
|||
await dropdown2.action('Enable'); |
|||
|
|||
await expect(ruleCard.locator('sqx-toggle .toggle-container')).toHaveAttribute('data-state', 'checked'); |
|||
await expect(ruleCard.root.locator('sqx-toggle .toggle-container')).toHaveAttribute('data-state', 'checked'); |
|||
}); |
|||
|
|||
test('edit rule', async ({ dropdown, page }) => { |
|||
const ruleName = await createRandomRule(page); |
|||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
|||
test('edit rule', async ({ page, rulePage, rulesPage }) => { |
|||
const ruleName = await createRandomRule(rulesPage, rulePage); |
|||
const ruleCard = await rulesPage.getRule(ruleName); |
|||
|
|||
await ruleCard.getByLabel('Options').click(); |
|||
const dropdown = await ruleCard.openOptionsDropdown(); |
|||
await dropdown.action('Edit'); |
|||
|
|||
await expect(page.getByText('Enabled')).toBeVisible(); |
|||
}); |
|||
|
|||
async function createRandomRule(page: Page) { |
|||
async function createRandomRule(rulesPage: RulesPage, rulePage: RulePage) { |
|||
const ruleName = `rule-${getRandomId()}`; |
|||
|
|||
await page.getByRole('link', { name: /New Rule/ }).click(); |
|||
|
|||
// Define rule action
|
|||
await page.getByText('Content changed').click(); |
|||
|
|||
// Define rule trigger
|
|||
await page.getByText('Webhook').click(); |
|||
// This is the only required field, so we have to enter some text.
|
|||
await page.locator('sqx-formattable-input').first().getByRole('textbox').fill('https:/squidex.io'); |
|||
|
|||
await page.getByRole('button', { name: 'Save' }).click(); |
|||
|
|||
await page.getByText('Enabled').waitFor({ state: 'visible' }); |
|||
await rulesPage.addRule(); |
|||
|
|||
// Go back
|
|||
await page.getByLabel('Back').click(); |
|||
await rulePage.selectContentChangedTrigger(); |
|||
await rulePage.selectWebhookAction(); |
|||
await rulePage.save(); |
|||
await rulePage.back(); |
|||
|
|||
// Define rule name.
|
|||
await page.locator('div.card', { hasText: /Unnamed Rule/ }).getByRole('heading').first().dblclick(); |
|||
await page.locator('form').getByRole('textbox').fill(ruleName); |
|||
await page.locator('form').getByLabel('Save').click(); |
|||
const rename = await rulesPage.renameRule(/Unnamed Rule/); |
|||
await rename.enterName(ruleName); |
|||
await rename.save(); |
|||
|
|||
return ruleName; |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Page } from '@playwright/test'; |
|||
|
|||
export class AppsPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async goto() { |
|||
await this.page.goto('/app'); |
|||
} |
|||
|
|||
public async gotoApp(name: string) { |
|||
await this.page.getByRole('heading', { name }).click(); |
|||
} |
|||
|
|||
public async openAppDialog() { |
|||
await this.page.getByTestId('new-app').click(); |
|||
|
|||
return new AppDialog(this.page); |
|||
} |
|||
} |
|||
|
|||
class AppDialog { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async enterName(name: string) { |
|||
await this.page.locator('#name').fill(name); |
|||
} |
|||
|
|||
public async save() { |
|||
await this.page.getByRole('button', { name: 'Create' }).click(); |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Locator, Page } from '@playwright/test'; |
|||
import { escapeRegex } from '../utils'; |
|||
|
|||
export class AssetsPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async goto(appName: string) { |
|||
await this.page.goto(`/app/${appName}/assets`); |
|||
} |
|||
|
|||
public async uploadFile(file: { name: string; mimeType: string; buffer: Buffer }) { |
|||
const fileChooserPromise = this.page.waitForEvent('filechooser'); |
|||
|
|||
await this.page.getByText(/Drop files here to upload/).locator('..').click(); |
|||
|
|||
const fileChooser = await fileChooserPromise; |
|||
await fileChooser.setFiles(file); |
|||
} |
|||
|
|||
public async getAssetCard(name: string) { |
|||
const locator = this.page.locator('sqx-asset', { hasText: escapeRegex(name) }); |
|||
|
|||
return new AssetCard(this.page, locator); |
|||
} |
|||
} |
|||
|
|||
export class AssetCard { |
|||
constructor(private readonly page: Page, |
|||
public readonly root: Locator, |
|||
) { |
|||
} |
|||
|
|||
public async delete(cancel = false) { |
|||
await this.root.getByLabel('Delete').click(); |
|||
|
|||
if (cancel) { |
|||
await this.page.getByRole('button', { name: /No/ }).click(); |
|||
} else { |
|||
await this.page.getByRole('button', { name: /Yes/ }).click(); |
|||
} |
|||
} |
|||
|
|||
public async edit() { |
|||
await this.root.getByLabel('Edit').click(); |
|||
|
|||
return new AssetDialog(this.page, this.page.getByTestId('dialog')); |
|||
} |
|||
} |
|||
|
|||
export class AssetDialog { |
|||
constructor(private readonly page: Page, |
|||
public readonly root: Locator, |
|||
) { |
|||
} |
|||
|
|||
public async enterName(name: string) { |
|||
await this.root.getByLabel('Name').fill(name); |
|||
} |
|||
|
|||
public async enterMetadata(name: string, value: string) { |
|||
const rows = this.root.getByPlaceholder('Name'); |
|||
await rows.first().waitFor({ state: 'attached' }); |
|||
|
|||
for (const row of await rows.all()) { |
|||
if (await row.inputValue() === name) { |
|||
await row.locator('../..').locator('input').nth(1).fill(value); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
throw new Error(`Cannot find row with name '${name}'`); |
|||
} |
|||
|
|||
public async save() { |
|||
await this.root.getByRole('button', { name: 'Save' }).click(); |
|||
await this.page.getByText('Asset has been updated.').waitFor({ state: 'visible' }); |
|||
await this.root.getByLabel('Close').click(); |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Page } from '@playwright/test'; |
|||
import { Dropdown } from './dropdown'; |
|||
|
|||
export class ContentPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async back() { |
|||
await this.page.getByLabel('Back').click(); |
|||
} |
|||
|
|||
public async enterField(value: string) { |
|||
await this.page.locator('sqx-field-editor').getByRole('textbox').fill(value); |
|||
} |
|||
|
|||
public async saveAndAdd() { |
|||
await this.page.getByLabel('Save', { exact: true }).getByLabel('More').click(); |
|||
|
|||
const dropdown = new Dropdown(this.page); |
|||
await dropdown.action('Save & add another'); |
|||
|
|||
await this.waitForCreation(); |
|||
} |
|||
|
|||
public async saveAndClose() { |
|||
await this.page.getByLabel('Save', { exact: true }).getByLabel('More').click(); |
|||
|
|||
const dropdown = new Dropdown(this.page); |
|||
await dropdown.action('Save & close'); |
|||
|
|||
await this.waitForCreation(); |
|||
} |
|||
|
|||
public async saveAndEdit() { |
|||
await this.page.getByRole('button', { name: 'Save', exact: true }).click(); |
|||
await this.waitForCreation(); |
|||
} |
|||
|
|||
public async savePublishAndAdd() { |
|||
await this.page.getByLabel('Save and Publish').getByLabel('More').click(); |
|||
|
|||
const dropdown = new Dropdown(this.page); |
|||
await dropdown.action('Save and Publish & add another'); |
|||
|
|||
await this.waitForCreation(); |
|||
} |
|||
|
|||
public async savePublishAndClose() { |
|||
await this.page.getByLabel('Save and Publish').getByLabel('More').click(); |
|||
|
|||
const dropdown = new Dropdown(this.page); |
|||
await dropdown.action('Save and Publish & close'); |
|||
|
|||
await this.waitForCreation(); |
|||
} |
|||
|
|||
public async savePublishAndEdit() { |
|||
await this.page.getByRole('button', { name: 'Save and Publish', exact: true }).click(); |
|||
await this.waitForCreation(); |
|||
} |
|||
|
|||
public async save() { |
|||
await this.page.getByRole('button', { name: 'Save', exact: true }).click(); |
|||
await this.waitForCreation(); |
|||
} |
|||
|
|||
public async openStatusDropdown(status: string) { |
|||
await this.page.getByRole('button', { name: status }).click(); |
|||
|
|||
return new Dropdown(this.page); |
|||
} |
|||
|
|||
public async openOptionsDropdown() { |
|||
await this.page.getByLabel('Options').click(); |
|||
|
|||
return new Dropdown(this.page); |
|||
} |
|||
|
|||
private async waitForCreation() { |
|||
await this.page.getByRole('alert').getByText('Content created successfully.').waitFor({ state: 'visible' }); |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Locator, Page } from '@playwright/test'; |
|||
import { escapeRegex } from '../utils'; |
|||
import { Dropdown } from './dropdown'; |
|||
|
|||
export class ContentsPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async goto(appName: string, schemaName: string) { |
|||
await this.page.goto(`/app/${appName}/content/${schemaName}`); |
|||
} |
|||
|
|||
public async increasePageSize() { |
|||
await this.page.getByRole('combobox').selectOption('3: 50'); |
|||
} |
|||
|
|||
public async addContent() { |
|||
await this.page.getByRole('button', { name: /New/ }).click(); |
|||
} |
|||
|
|||
public async changeSelectedStatus(status: string) { |
|||
await this.page.getByRole('button', { name: status }).click(); |
|||
await this.page.getByRole('button', { name: 'Confirm' }).click(); |
|||
} |
|||
|
|||
public async deleteSelected() { |
|||
await this.page.getByRole('button', { name: 'Delete' }).click(); |
|||
await this.page.getByRole('button', { name: 'Yes' }).click(); |
|||
} |
|||
|
|||
public async getContentRow(text: string) { |
|||
const locator = this.page.locator('tr', { hasText: escapeRegex(text) }); |
|||
|
|||
return new ContentRow(this.page, locator); |
|||
} |
|||
} |
|||
|
|||
export class ContentRow { |
|||
constructor(private readonly page: Page, |
|||
public readonly root: Locator, |
|||
) { |
|||
} |
|||
|
|||
public async edit() { |
|||
await this.root.click(); |
|||
} |
|||
|
|||
public async select() { |
|||
await this.root.getByRole('checkbox').click(); |
|||
} |
|||
|
|||
public async openOptionsDropdown() { |
|||
await this.root.getByLabel('Options').click(); |
|||
|
|||
return new Dropdown(this.page); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Page } from '@playwright/test'; |
|||
|
|||
export class Dropdown { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async delete(cancel = false) { |
|||
await this.page.getByText('Delete').click(); |
|||
|
|||
if (cancel) { |
|||
await this.page.getByRole('button', { name: /No/ }).click(); |
|||
} else { |
|||
await this.page.getByRole('button', { name: /Yes/ }).click(); |
|||
} |
|||
} |
|||
|
|||
public async action(name: string) { |
|||
await this.page.getByText(name).click(); |
|||
await this.page.locator('sqx-dropdown-menu').waitFor({ state: 'hidden' }); |
|||
} |
|||
|
|||
public async actionConfirm(name: string) { |
|||
await this.action(name); |
|||
await this.page.getByRole('button', { name: 'Confirm' }).click(); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
export * from './apps'; |
|||
export * from './assets'; |
|||
export * from './content'; |
|||
export * from './contents'; |
|||
export * from './login'; |
|||
export * from './rule'; |
|||
export * from './rule'; |
|||
export * from './rules'; |
|||
export * from './schema'; |
|||
export * from './schemas'; |
|||
@ -0,0 +1,47 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Page } from '@playwright/test'; |
|||
|
|||
export class LoginPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async goto() { |
|||
await this.page.goto('/'); |
|||
} |
|||
|
|||
public async openPopup() { |
|||
const popupPromise = this.page.waitForEvent('popup'); |
|||
|
|||
await this.page.getByTestId('login').click(); |
|||
|
|||
const popup = await popupPromise; |
|||
await popup.waitForLoadState(); |
|||
|
|||
await popup.getByTestId('login-button').waitFor(); |
|||
return new LoginPopup(popup); |
|||
} |
|||
} |
|||
|
|||
export class LoginPopup { |
|||
constructor( |
|||
public readonly root: Page, |
|||
) { |
|||
} |
|||
|
|||
public async enterEmail(email: string) { |
|||
await this.root.getByPlaceholder('Enter Email').fill(email); |
|||
} |
|||
|
|||
public async enterPassword(password: string) { |
|||
await this.root.getByPlaceholder('Enter Password').fill(password); |
|||
} |
|||
|
|||
public async login() { |
|||
await this.root.getByTestId('login-button').click(); |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Page } from '@playwright/test'; |
|||
|
|||
export class RulePage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async selectContentChangedTrigger() { |
|||
await this.page.getByText('Content changed').click(); |
|||
} |
|||
|
|||
public async selectWebhookAction() { |
|||
await this.page.getByText('Webhook').click(); |
|||
await this.page.locator('sqx-formattable-input').first().getByRole('textbox').fill('https:/squidex.io'); |
|||
} |
|||
|
|||
public async save() { |
|||
await this.page.getByRole('button', { name: 'Save' }).click(); |
|||
await this.page.getByText('Enabled').waitFor({ state: 'visible' }); |
|||
} |
|||
|
|||
public async back() { |
|||
await this.page.getByLabel('Back').click(); |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Locator, Page } from '@playwright/test'; |
|||
import { escapeRegex } from '../utils'; |
|||
import { Dropdown } from './dropdown'; |
|||
|
|||
export class RulesPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async goto(appName: string) { |
|||
await this.page.goto(`/app/${appName}/rules`); |
|||
} |
|||
|
|||
public async addRule() { |
|||
await this.page.getByRole('link', { name: /New Rule/ }).click(); |
|||
} |
|||
|
|||
public async renameRule(name: RegExp) { |
|||
await this.page.locator('div.card', { hasText: name }).getByRole('heading').first().dblclick(); |
|||
|
|||
return new RenameDialog(this.page); |
|||
} |
|||
|
|||
public async getRule(name: string) { |
|||
const locator = this.page.locator('div.card', { hasText: escapeRegex(name) }); |
|||
|
|||
return new RuleCard(this.page, locator); |
|||
} |
|||
} |
|||
|
|||
export class RuleCard { |
|||
constructor(private readonly page: Page, |
|||
public readonly root: Locator, |
|||
) { |
|||
} |
|||
|
|||
public async openOptionsDropdown() { |
|||
await this.root.getByLabel('Options').click(); |
|||
|
|||
return new Dropdown(this.page); |
|||
} |
|||
} |
|||
|
|||
export class RenameDialog { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async enterName(name: string) { |
|||
await this.page.locator('form').getByRole('textbox').fill(name); |
|||
} |
|||
|
|||
public async save() { |
|||
await this.page.locator('form').getByLabel('Save').click(); |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { expect, Locator, Page } from '@playwright/test'; |
|||
import { escapeRegex } from '../utils'; |
|||
import { Dropdown } from './dropdown'; |
|||
|
|||
export class SchemaPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async goto(appName: string, schemaName: string) { |
|||
await this.page.goto(`/app/${appName}/schemas/${schemaName}`); |
|||
} |
|||
|
|||
public async openFieldWizard() { |
|||
await this.page.locator('button').filter({ hasText: /^Add Field$/ }).click(); |
|||
|
|||
return new FieldDialog(this.page, this.page.getByTestId('dialog')); |
|||
} |
|||
|
|||
public async openNestedFieldWizard() { |
|||
await this.page.locator('button').filter({ hasText: /Add Nested Field/ }).click(); |
|||
|
|||
return new FieldDialog(this.page, this.page.getByTestId('dialog')); |
|||
} |
|||
|
|||
public async openOptionsDropdown() { |
|||
await this.page.getByLabel('Options').click(); |
|||
|
|||
return new Dropdown(this.page); |
|||
} |
|||
|
|||
public async getFieldRow(fieldName: string) { |
|||
const locator = this.page.locator('div.table-items-row-summary', { hasText: escapeRegex(fieldName) }); |
|||
|
|||
return new FieldRow(this.page, locator); |
|||
} |
|||
|
|||
public async publish() { |
|||
const button = this.page.getByRole('button', { name: 'Published', exact: true }); |
|||
await button.click(); |
|||
|
|||
await expect(button).toBeDisabled(); |
|||
} |
|||
|
|||
public async unpublish() { |
|||
const button = this.page.getByRole('button', { name: 'Unpublished', exact: true }); |
|||
await button.click(); |
|||
|
|||
await expect(button).toBeDisabled(); |
|||
} |
|||
} |
|||
|
|||
export class FieldRow { |
|||
constructor(private readonly page: Page, |
|||
public readonly root: Locator, |
|||
) { |
|||
} |
|||
|
|||
public async openOptionsDropdown() { |
|||
await this.root.getByLabel('Options').click(); |
|||
|
|||
return new Dropdown(this.page); |
|||
} |
|||
} |
|||
|
|||
export class FieldDialog { |
|||
constructor(private readonly page: Page, |
|||
public readonly root: Locator, |
|||
) {} |
|||
|
|||
public async enterName(name: string) { |
|||
await this.root.getByPlaceholder('Enter field name').fill(name); |
|||
} |
|||
|
|||
public async enterType(type: string) { |
|||
await this.root.getByText(type, { exact: true }).click(); |
|||
} |
|||
|
|||
public async enterLabel(label: string) { |
|||
await this.root.getByLabel('Label').fill(label); |
|||
} |
|||
|
|||
public async createAndClose() { |
|||
await this.root.getByRole('button', { name: 'Create' }).click(); |
|||
} |
|||
|
|||
public async createAndAdd() { |
|||
await this.root.getByLabel('Add field').getByLabel('More').click(); |
|||
|
|||
const dropdown = new Dropdown(this.page); |
|||
await dropdown.action('Create & add another'); |
|||
} |
|||
|
|||
public async createAndEdit() { |
|||
await this.root.getByLabel('Add field').getByLabel('More').click(); |
|||
|
|||
const dropdown = new Dropdown(this.page); |
|||
await dropdown.action('Create & edit properties'); |
|||
} |
|||
|
|||
public async saveAndClose() { |
|||
await this.root.getByRole('button', { name: 'Save and close' }).click(); |
|||
} |
|||
|
|||
public async saveAndAdd() { |
|||
await this.root.getByLabel('Save field').getByLabel('More').click(); |
|||
|
|||
const dropdown = new Dropdown(this.page); |
|||
await dropdown.action('Save and add field'); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Locator, Page } from '@playwright/test'; |
|||
import { escapeRegex } from '../utils'; |
|||
|
|||
export class SchemasPage { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async goto(appName: string) { |
|||
await this.page.goto(`/app/${appName}/schemas`); |
|||
} |
|||
|
|||
public async getSchemaLink(schemaName: string) { |
|||
const locator = this.page.locator('a.nav-link', { hasText: escapeRegex(schemaName) }); |
|||
|
|||
return new SchemaLink(this.page, locator); |
|||
} |
|||
|
|||
public async openSchemaDialog() { |
|||
await this.page.getByLabel('Create Schema').click(); |
|||
|
|||
return new CreateDialog(this.page); |
|||
} |
|||
} |
|||
|
|||
export class SchemaLink { |
|||
constructor(private readonly page: Page, |
|||
public readonly root: Locator, |
|||
) { |
|||
} |
|||
} |
|||
|
|||
export class CreateDialog { |
|||
constructor(private readonly page: Page) {} |
|||
|
|||
public async enterName(name: string) { |
|||
await this.page.getByLabel('Name (required)').fill(name); |
|||
} |
|||
|
|||
public async save() { |
|||
await this.page.getByRole('button', { name: 'Create', exact: true }).click(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue