-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tests(e2e): improve login util stability #20223
Changes from 11 commits
5adf218
9274ffd
777252f
3ad0c77
9044fdc
cf2e764
afc823e
d44bb3a
fd2baaf
f3c4338
922ac34
15f1ec2
7b65b04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
'use strict'; | ||
|
||
const { CUSTOM_TRANSFER_TOKEN_ACCESS_KEY } = require('./app-template/template/src/constants'); | ||
|
||
// NOTE: anything included here needs to be included in all test datasets exports | ||
|
@@ -41,9 +43,17 @@ const ALLOWED_CONTENT_TYPES = [ | |
const ADMIN_EMAIL_ADDRESS = '[email protected]'; | ||
const ADMIN_PASSWORD = 'Testing123!'; | ||
|
||
const TITLE_LOGIN = 'Strapi Admin'; | ||
const TITLE_HOME = 'Homepage | Strapi'; | ||
|
||
const URL_ROOT = '/admin'; | ||
|
||
module.exports = { | ||
ADMIN_EMAIL_ADDRESS, | ||
ADMIN_PASSWORD, | ||
ALLOWED_CONTENT_TYPES, | ||
CUSTOM_TRANSFER_TOKEN_ACCESS_KEY, | ||
TITLE_LOGIN, | ||
TITLE_HOME, | ||
URL_ROOT, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { test, expect } from '@playwright/test'; | ||
import { test, expect, Page } from '@playwright/test'; | ||
import { login } from '../../utils/login'; | ||
import { navToHeader } from '../../utils/shared'; | ||
import { resetDatabaseAndImportDataFromPath } from '../../utils/dts-import'; | ||
|
@@ -22,12 +22,19 @@ const createAPIToken = async (page, tokenName, duration, type) => { | |
}; | ||
|
||
test.describe('API Tokens', () => { | ||
test.beforeEach(async ({ page }) => { | ||
let page: Page; | ||
|
||
// For some reason, logging in after each token is extremely flaky, so we'll just do it once | ||
test.beforeAll(async ({ browser }) => { | ||
await resetDatabaseAndImportDataFromPath('with-admin.tar'); | ||
await page.goto('/admin'); | ||
page = await browser.newPage(); | ||
await login({ page }); | ||
}); | ||
|
||
test.afterAll(async ({ browser }) => { | ||
page.close(); | ||
}); | ||
|
||
// Test token creation | ||
const testCases = [ | ||
['30-day Read-only token', '30 days', 'Read-only'], | ||
|
@@ -37,16 +44,25 @@ test.describe('API Tokens', () => { | |
['unlimited token', 'Unlimited', 'Full access'], | ||
]; | ||
for (const [name, duration, type] of testCases) { | ||
test(`A user should be able to create a ${name}`, async ({ page }) => { | ||
test(`A user should be able to create a ${name}`, async ({}) => { | ||
await createAPIToken(page, name, duration, type); | ||
}); | ||
} | ||
|
||
test('Created tokens list page should be correct', async ({ page }) => { | ||
test('Created tokens list page should be correct', async ({}) => { | ||
await createAPIToken(page, 'my test token', 'unlimited', 'Full access'); | ||
|
||
// if we don't wait until createdAt is at least 1s, we see "NaN" for the timestamp | ||
// TODO: fix the bug and remove this | ||
await page.waitForTimeout(1100); | ||
|
||
await navToHeader(page, ['Settings', 'API Tokens'], 'API Tokens'); | ||
|
||
const row = page.getByRole('gridcell', { name: 'my test token', exact: true }); | ||
await expect(row).toBeVisible(); | ||
const nameCell = page.getByRole('gridcell', { name: 'my test token', exact: true }); | ||
await expect(nameCell).toBeVisible(); | ||
|
||
// Locate the parent of nameCell and then search within it for the timestamp | ||
const parentRow = nameCell.locator('xpath=..'); | ||
await expect(parentRow).toContainText(/\d+ (second|minute)s? ago/); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. adds check present on transfer tokens that was missing from api tokens |
||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { test, expect } from '@playwright/test'; | ||
import { resetDatabaseAndImportDataFromPath } from '../../utils/dts-import'; | ||
import { toggleRateLimiting } from '../../utils/rate-limit'; | ||
import { ADMIN_EMAIL_ADDRESS, ADMIN_PASSWORD } from '../../constants'; | ||
import { ADMIN_EMAIL_ADDRESS, ADMIN_PASSWORD, TITLE_HOME, TITLE_LOGIN } from '../../constants'; | ||
import { login } from '../../utils/login'; | ||
|
||
test.describe('Login', () => { | ||
|
@@ -14,26 +14,33 @@ test.describe('Login', () => { | |
test('A user should be able to log in with or without making their authentication persistent', async ({ | ||
page, | ||
context, | ||
browser, | ||
}) => { | ||
// Test without making user authentication persistent | ||
await login({ page }); | ||
await expect(page).toHaveTitle('Homepage | Strapi'); | ||
await expect(page).toHaveTitle(TITLE_HOME); | ||
|
||
// Close the page and open a new one | ||
await page.close(); | ||
|
||
page = await context.newPage(); | ||
await page.goto('/admin'); | ||
await expect(page).toHaveTitle('Strapi Admin'); | ||
const nonPersistentPage = await context.newPage(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re-using the page object was rarely but sometimes causing a "page already closed" when attempting to navigate after |
||
await Promise.all([ | ||
nonPersistentPage.waitForLoadState('networkidle'), | ||
nonPersistentPage.goto('/admin'), | ||
]); | ||
await expect(nonPersistentPage).toHaveTitle(TITLE_LOGIN); | ||
|
||
// Test with making user authentication persistent | ||
await login({ page, rememberMe: true }); | ||
await expect(page).toHaveTitle('Homepage | Strapi'); | ||
|
||
await page.close(); | ||
|
||
page = await context.newPage(); | ||
await page.goto('/admin'); | ||
await expect(page).toHaveTitle('Homepage | Strapi'); | ||
await login({ page: nonPersistentPage, rememberMe: true }); | ||
await expect(nonPersistentPage).toHaveTitle(TITLE_HOME); | ||
|
||
// Close the page and open a new one | ||
await nonPersistentPage.close(); | ||
const persistentPage = await context.newPage(); | ||
await Promise.all([ | ||
persistentPage.waitForLoadState('networkidle'), | ||
persistentPage.goto('/admin'), | ||
]); | ||
await expect(persistentPage).toHaveTitle(TITLE_HOME); | ||
}); | ||
}); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
import { resetDatabaseAndImportDataFromPath } from '../../utils/dts-import'; | ||
import { ADMIN_EMAIL_ADDRESS, ADMIN_PASSWORD } from '../../constants'; | ||
import { ADMIN_EMAIL_ADDRESS, ADMIN_PASSWORD, TITLE_HOME } from '../../constants'; | ||
|
||
/** | ||
* Fill in the sign up form with valid values | ||
|
@@ -28,7 +28,7 @@ test.describe('Sign Up', () => { | |
await resetDatabaseAndImportDataFromPath('without-admin.tar', (cts) => | ||
cts.filter((ct) => ct !== 'plugin::i18n.locale') | ||
); | ||
await page.goto('/admin'); | ||
await page.goto('/admin', { waitUntil: 'networkidle' }); | ||
await fillValidSignUpForm({ page }); | ||
}); | ||
|
||
|
@@ -96,8 +96,11 @@ test.describe('Sign Up', () => { | |
test('a user should be able to signup when the strapi instance starts fresh', async ({ | ||
page, | ||
}) => { | ||
await page.getByRole('button', { name: "Let's start" }).click(); | ||
await Promise.all([ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using Promise.all with waitForLoadState and a click action works better to give clicks the time necessary to load the next page |
||
page.waitForLoadState('networkidle'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should make networkidle a const somewhere, it gets used quite a bit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since it's a typed value for waitForLoadState I'll leave it for now, but I actually think the next time I'm in here messing with things I'm going to add a util for navigating + waiting so you can just call one line instead of 2+, nobody should have to remember to do this to write a stable test |
||
page.getByRole('button', { name: "Let's start" }).click(), | ||
]); | ||
|
||
await expect(page).toHaveTitle('Homepage | Strapi'); | ||
await expect(page).toHaveTitle(TITLE_HOME); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { test, expect } from '@playwright/test'; | ||
import { test, expect, Page } from '@playwright/test'; | ||
import { login } from '../../utils/login'; | ||
import { resetDatabaseAndImportDataFromPath } from '../../utils/dts-import'; | ||
import { navToHeader } from '../../utils/shared'; | ||
|
@@ -26,12 +26,19 @@ const createTransferToken = async (page, tokenName, duration, type) => { | |
}; | ||
|
||
test.describe('Transfer Tokens', () => { | ||
test.beforeEach(async ({ page }) => { | ||
let page: Page; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a pattern we want to promote? you effectively make the last test dependent on the previous because you're not starting with a "fresh" slate every time. I'd be cautious about promoting this pattern as I don't think it's something we want to get into the habit of doing in the long term? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
My point exactly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately I think the bad practice is the core of the fix here. I will take a look and see if there's an alternative I can find, but otherwise I may just add a warning not to repeat that pattern in other tests. |
||
// For some reason, logging in after each token is extremely flaky, so we'll just do it once | ||
test.beforeAll(async ({ browser }) => { | ||
await resetDatabaseAndImportDataFromPath('with-admin.tar'); | ||
await page.goto('/admin'); | ||
page = await browser.newPage(); | ||
await login({ page }); | ||
}); | ||
|
||
test.afterAll(async ({ browser }) => { | ||
page.close(); | ||
}); | ||
|
||
// Test token creation | ||
const testCases = [ | ||
['30-day push token', '30 days', 'Push'], | ||
|
@@ -43,12 +50,12 @@ test.describe('Transfer Tokens', () => { | |
['unlimited token', 'Unlimited', 'Full access'], | ||
]; | ||
for (const [name, duration, type] of testCases) { | ||
test(`A user should be able to create a ${name}`, async ({ page }) => { | ||
test(`A user should be able to create a ${name}`, async ({}) => { | ||
await createTransferToken(page, name, duration, type); | ||
}); | ||
} | ||
|
||
test('Created tokens list page should be correct', async ({ page }) => { | ||
test('Created tokens list page should be correct', async ({}) => { | ||
await createTransferToken(page, 'my test token', 'unlimited', 'Full access'); | ||
|
||
// if we don't wait until createdAt is at least 1s, we see "NaN" for the timestamp | ||
|
@@ -57,9 +64,13 @@ test.describe('Transfer Tokens', () => { | |
|
||
await navToHeader(page, ['Settings', 'Transfer Tokens'], 'Transfer Tokens'); | ||
|
||
const row = page.getByRole('gridcell', { name: 'my test token', exact: true }); | ||
await expect(row).toBeVisible(); | ||
await expect(page.getByText(/\d+ (second|minute)s? ago/)).toBeVisible(); | ||
const nameCell = page.getByRole('gridcell', { name: 'my test token', exact: true }); | ||
await expect(nameCell).toBeVisible(); | ||
|
||
// Locate the parent of nameCell and then search within it for the timestamp | ||
const parentRow = nameCell.locator('xpath=..'); | ||
await expect(parentRow).toContainText(/\d+ (second|minute)s? ago/); | ||
|
||
Comment on lines
+70
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we aren't clearing the db after each run anymore and have multiple tokens, we need to be more specific with what we're looking for |
||
// TODO: expand on this test, it could check edit and delete icons | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so we stop having so many conflicts merging
develop
<->v5/main