chore: unflake auth e2e tests
This commit is contained in:
@@ -51,7 +51,8 @@ module.exports = {
|
|||||||
) {
|
) {
|
||||||
context.report({
|
context.report({
|
||||||
node: node.callee.property,
|
node: node.callee.property,
|
||||||
message: 'Non-retryable assertion used in Playwright test: "{{ assertion }}"',
|
message:
|
||||||
|
'Non-retryable, flaky assertion used in Playwright test: "{{ assertion }}". Those need to be wrapped in expect.poll or expect.toPass.',
|
||||||
data: {
|
data: {
|
||||||
assertion: node.callee.property.name,
|
assertion: node.callee.property.name,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ module.exports = {
|
|||||||
'jest/require-top-level-describe': 'off',
|
'jest/require-top-level-describe': 'off',
|
||||||
'jest-dom/prefer-to-have-attribute': 'off',
|
'jest-dom/prefer-to-have-attribute': 'off',
|
||||||
'playwright/prefer-web-first-assertions': 'error',
|
'playwright/prefer-web-first-assertions': 'error',
|
||||||
'payload/no-non-retryable-assertions': 'error',
|
// Enable the no-non-retryable-assertions rule ONLY for hunting for flakes
|
||||||
|
// 'payload/no-non-retryable-assertions': 'error',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -103,12 +103,19 @@ describe('auth', () => {
|
|||||||
await page.locator('#field-enableAPIKey').click()
|
await page.locator('#field-enableAPIKey').click()
|
||||||
|
|
||||||
// assert that the value is set
|
// assert that the value is set
|
||||||
const apiKey = await page.locator('#apiKey').inputValue()
|
const apiKeyLocator = page.locator('#apiKey')
|
||||||
expect(apiKey).toBeDefined()
|
await expect
|
||||||
|
.poll(async () => await apiKeyLocator.inputValue(), { timeout: 45000 })
|
||||||
|
.toBeDefined()
|
||||||
|
|
||||||
await saveDocAndAssert(page)
|
await saveDocAndAssert(page)
|
||||||
|
|
||||||
expect(await page.locator('#apiKey').inputValue()).toStrictEqual(apiKey)
|
await expect(async () => {
|
||||||
|
const apiKey = await apiKeyLocator.inputValue()
|
||||||
|
expect(await page.locator('#apiKey').inputValue()).toStrictEqual(apiKey)
|
||||||
|
}).toPass({
|
||||||
|
timeout: 45000,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should disable api key', async () => {
|
test('should disable api key', async () => {
|
||||||
@@ -123,14 +130,18 @@ describe('auth', () => {
|
|||||||
await saveDocAndAssert(page)
|
await saveDocAndAssert(page)
|
||||||
|
|
||||||
// use the api key in a fetch to assert that it is disabled
|
// use the api key in a fetch to assert that it is disabled
|
||||||
const response = await fetch(`${apiURL}/${apiKeysSlug}/me`, {
|
await expect(async () => {
|
||||||
headers: {
|
const response = await fetch(`${apiURL}/${apiKeysSlug}/me`, {
|
||||||
...headers,
|
headers: {
|
||||||
Authorization: `${slug} API-Key ${user.apiKey}`,
|
...headers,
|
||||||
},
|
Authorization: `${slug} API-Key ${user.apiKey}`,
|
||||||
}).then((res) => res.json())
|
},
|
||||||
|
}).then((res) => res.json())
|
||||||
|
|
||||||
expect(response.user).toBeNull()
|
expect(response.user).toBeNull()
|
||||||
|
}).toPass({
|
||||||
|
timeout: 45000,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export async function delayNetwork({
|
|||||||
context: BrowserContext
|
context: BrowserContext
|
||||||
delay: 'Fast 3G' | 'Slow 3G' | 'Slow 4G'
|
delay: 'Fast 3G' | 'Slow 3G' | 'Slow 4G'
|
||||||
page: Page
|
page: Page
|
||||||
}): Promise<void> {
|
}) {
|
||||||
const cdpSession = await context.newCDPSession(page)
|
const cdpSession = await context.newCDPSession(page)
|
||||||
|
|
||||||
await cdpSession.send('Network.emulateNetworkConditions', {
|
await cdpSession.send('Network.emulateNetworkConditions', {
|
||||||
@@ -91,8 +91,7 @@ export async function saveDocHotkeyAndAssert(page: Page): Promise<void> {
|
|||||||
export async function saveDocAndAssert(page: Page, selector = '#action-save'): Promise<void> {
|
export async function saveDocAndAssert(page: Page, selector = '#action-save'): Promise<void> {
|
||||||
await page.click(selector, { delay: 100 })
|
await page.click(selector, { delay: 100 })
|
||||||
await expect(page.locator('.Toastify')).toContainText('successfully')
|
await expect(page.locator('.Toastify')).toContainText('successfully')
|
||||||
await wait(500)
|
await expect.poll(() => page.url(), { timeout: 45000 }).not.toContain('create')
|
||||||
expect(page.url()).not.toContain('create')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openNav(page: Page): Promise<void> {
|
export async function openNav(page: Page): Promise<void> {
|
||||||
@@ -135,13 +134,24 @@ export function exactText(text: string) {
|
|||||||
return new RegExp(`^${text}$`)
|
return new RegExp(`^${text}$`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkPageTitle = async (page: Page, title: string) =>
|
export const checkPageTitle = async (page: Page, title: string) => {
|
||||||
expect(await page.locator('.doc-header__title.render-title')?.first()?.innerText()).toBe(title)
|
await expect
|
||||||
|
.poll(async () => await page.locator('.doc-header__title.render-title')?.first()?.innerText(), {
|
||||||
|
timeout: 45000,
|
||||||
|
})
|
||||||
|
.toBe(title)
|
||||||
|
}
|
||||||
|
|
||||||
export const checkBreadcrumb = async (page: Page, text: string) =>
|
export const checkBreadcrumb = async (page: Page, text: string) => {
|
||||||
expect(await page.locator('.step-nav.app-header__step-nav .step-nav__last')?.innerText()).toBe(
|
await expect
|
||||||
text,
|
.poll(
|
||||||
)
|
async () => await page.locator('.step-nav.app-header__step-nav .step-nav__last')?.innerText(),
|
||||||
|
{
|
||||||
|
timeout: 45000,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.toBe(text)
|
||||||
|
}
|
||||||
|
|
||||||
export const selectTableRow = async (page: Page, title: string): Promise<void> => {
|
export const selectTableRow = async (page: Page, title: string): Promise<void> => {
|
||||||
const selector = `tbody tr:has-text("${title}") .select-row__checkbox input[type=checkbox]`
|
const selector = `tbody tr:has-text("${title}") .select-row__checkbox input[type=checkbox]`
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ export default defineConfig({
|
|||||||
// Look for test files in the "test" directory, relative to this configuration file
|
// Look for test files in the "test" directory, relative to this configuration file
|
||||||
testDir: '',
|
testDir: '',
|
||||||
testMatch: '*e2e.spec.ts',
|
testMatch: '*e2e.spec.ts',
|
||||||
timeout: 180000, // 3 minutes
|
timeout: 240000, // 4 minutes
|
||||||
use: {
|
use: {
|
||||||
screenshot: 'only-on-failure',
|
screenshot: 'only-on-failure',
|
||||||
trace: 'retain-on-failure',
|
trace: 'retain-on-failure',
|
||||||
video: 'retain-on-failure',
|
video: 'retain-on-failure',
|
||||||
},
|
},
|
||||||
expect: {
|
expect: {
|
||||||
timeout: 45000,
|
timeout: 60000,
|
||||||
},
|
},
|
||||||
workers: 16,
|
workers: 16,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user