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 { expect } from '@playwright/test'; |
||||
import { escapeRegex, getRandomId } from '../utils'; |
import { RulePage, RulesPage } from '../pages'; |
||||
|
import { getRandomId } from '../utils'; |
||||
import { test } from './_fixture'; |
import { test } from './_fixture'; |
||||
|
|
||||
// We have no easy way to identity rules. Therefore run them sequentially.
|
// We have no easy way to identity rules. Therefore run them sequentially.
|
||||
test.describe.configure({ mode: 'serial' }); |
test.describe.configure({ mode: 'serial' }); |
||||
|
|
||||
test.beforeEach(async ({ page, appName }) => { |
test.beforeEach(async ({ appName, rulesPage }) => { |
||||
await page.goto(`/app/${appName}/rules`); |
await rulesPage.goto(appName); |
||||
}); |
}); |
||||
|
|
||||
test('create rule', async ({ page }) => { |
test('create rule', async ({ rulesPage, rulePage }) => { |
||||
const ruleName = await createRandomRule(page); |
const ruleName = await createRandomRule(rulesPage, rulePage); |
||||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
const ruleCard = await rulesPage.getRule(ruleName); |
||||
|
|
||||
await expect(ruleCard).toBeVisible(); |
await expect(ruleCard.root).toBeVisible(); |
||||
}); |
}); |
||||
|
|
||||
test('delete rule', async ({ dropdown, page }) => { |
test('delete rule', async ({ rulesPage, rulePage }) => { |
||||
const ruleName = await createRandomRule(page); |
const ruleName = await createRandomRule(rulesPage, rulePage); |
||||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
const ruleCard = await rulesPage.getRule(ruleName); |
||||
|
|
||||
await ruleCard.getByLabel('Options').click(); |
const dropdown = await ruleCard.openOptionsDropdown(); |
||||
await dropdown.delete(); |
await dropdown.delete(); |
||||
|
|
||||
await expect(ruleCard).not.toBeVisible(); |
await expect(ruleCard.root).not.toBeVisible(); |
||||
}); |
}); |
||||
|
|
||||
test('disable rule', async ({ dropdown, page }) => { |
test('disable rule', async ({ rulePage, rulesPage }) => { |
||||
const ruleName = await createRandomRule(page); |
const ruleName = await createRandomRule(rulesPage, rulePage); |
||||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
const ruleCard = await rulesPage.getRule(ruleName); |
||||
|
|
||||
await ruleCard.getByLabel('Options').click(); |
const dropdown = await ruleCard.openOptionsDropdown(); |
||||
await dropdown.action('Disable'); |
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 }) => { |
test('enable rule', async ({ rulePage, rulesPage }) => { |
||||
const ruleName = await createRandomRule(page); |
const ruleName = await createRandomRule(rulesPage, rulePage); |
||||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
const ruleCard = await rulesPage.getRule(ruleName); |
||||
|
|
||||
await ruleCard.getByLabel('Options').click(); |
const dropdown1 = await ruleCard.openOptionsDropdown(); |
||||
await dropdown.action('Disable'); |
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(); |
const dropdown2 = await ruleCard.openOptionsDropdown(); |
||||
await dropdown.action('Enable'); |
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 }) => { |
test('edit rule', async ({ page, rulePage, rulesPage }) => { |
||||
const ruleName = await createRandomRule(page); |
const ruleName = await createRandomRule(rulesPage, rulePage); |
||||
const ruleCard = page.locator('div.card', { hasText: escapeRegex(ruleName) }); |
const ruleCard = await rulesPage.getRule(ruleName); |
||||
|
|
||||
await ruleCard.getByLabel('Options').click(); |
const dropdown = await ruleCard.openOptionsDropdown(); |
||||
await dropdown.action('Edit'); |
await dropdown.action('Edit'); |
||||
|
|
||||
await expect(page.getByText('Enabled')).toBeVisible(); |
await expect(page.getByText('Enabled')).toBeVisible(); |
||||
}); |
}); |
||||
|
|
||||
async function createRandomRule(page: Page) { |
async function createRandomRule(rulesPage: RulesPage, rulePage: RulePage) { |
||||
const ruleName = `rule-${getRandomId()}`; |
const ruleName = `rule-${getRandomId()}`; |
||||
|
|
||||
await page.getByRole('link', { name: /New Rule/ }).click(); |
await rulesPage.addRule(); |
||||
|
|
||||
// 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' }); |
|
||||
|
|
||||
// Go back
|
await rulePage.selectContentChangedTrigger(); |
||||
await page.getByLabel('Back').click(); |
await rulePage.selectWebhookAction(); |
||||
|
await rulePage.save(); |
||||
|
await rulePage.back(); |
||||
|
|
||||
// Define rule name.
|
const rename = await rulesPage.renameRule(/Unnamed Rule/); |
||||
await page.locator('div.card', { hasText: /Unnamed Rule/ }).getByRole('heading').first().dblclick(); |
await rename.enterName(ruleName); |
||||
await page.locator('form').getByRole('textbox').fill(ruleName); |
await rename.save(); |
||||
await page.locator('form').getByLabel('Save').click(); |
|
||||
|
|
||||
return ruleName; |
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