Skip to content

Commit 0145920

Browse files
authored
e2e tests (#16533)
In this PR, - current basic E2E tests are fixed, and some were added, covering some basic scenarios - some tests avec been commented out, until we decide whether they are worth fixing The next steps are - evaluate the flakiness of the tests. Once they've proved not to be flaky, we should add more tests + re-write the current ones not using aria-label (cf @lucasbordeau indication). - We will add them back to the development flow
1 parent 1cbbd04 commit 0145920

File tree

29 files changed

+800
-464
lines changed

29 files changed

+800
-464
lines changed

.github/workflows/ci-e2e.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
- name: Setup environment files
7979
run: |
8080
cp packages/twenty-front/.env.example packages/twenty-front/.env
81-
npx nx reset:env twenty-server
81+
npx nx reset:env:e2e-testing-server twenty-server
8282
8383
- name: Build frontend
8484
run: NODE_ENV=production NODE_OPTIONS="--max-old-space-size=10240" npx nx build twenty-front
@@ -103,7 +103,7 @@ jobs:
103103
npm_config_yes=true npx serve -s packages/twenty-front/build -l 3001 &
104104
echo "Waiting for frontend to be ready..."
105105
timeout 60 bash -c 'until curl -s http://localhost:3001; do sleep 2; done'
106-
106+
107107
- name: Start worker
108108
run: |
109109
npx nx run twenty-server:worker &

packages/twenty-e2e-testing/.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ FRONTEND_BASE_URL=http://localhost:3001
33
BACKEND_BASE_URL=http://localhost:3000
44
DEFAULT_LOGIN=tim@apple.dev
55
DEFAULT_PASSWORD=tim@apple.dev
6-
WEBSITE_URL=https://twenty.com
6+
WEBSITE_URL=https://twenty.com

packages/twenty-e2e-testing/lib/fixtures/screenshot.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const test = base.extend<{ screenshotHook: void }>({
2727
},
2828
{ auto: true },
2929
],
30+
baseURL: process.env.LINK ? new URL(process.env.LINK).origin : 'http://localhost:3001',
3031
});
3132

3233
export { expect } from '@playwright/test';

packages/twenty-e2e-testing/lib/requests/create-workflow.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Page } from '@playwright/test';
2-
import { getAuthToken } from '../utils/getAuthToken';
1+
import { type Page } from '@playwright/test';
2+
import { getAccessAuthToken } from '../utils/getAccessAuthToken';
33
import { backendGraphQLUrl } from './backend';
44

55
export const createWorkflow = async ({
@@ -11,7 +11,7 @@ export const createWorkflow = async ({
1111
workflowId: string;
1212
workflowName: string;
1313
}) => {
14-
const { authToken } = await getAuthToken(page);
14+
const { authToken } = await getAccessAuthToken(page);
1515

1616
return page.request.post(backendGraphQLUrl, {
1717
headers: {

packages/twenty-e2e-testing/lib/requests/delete-workflow.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Page } from '@playwright/test';
2-
import { getAuthToken } from '../utils/getAuthToken';
1+
import { type Page } from '@playwright/test';
2+
import { getAccessAuthToken } from '../utils/getAccessAuthToken';
33
import { backendGraphQLUrl } from './backend';
44

55
export const deleteWorkflow = async ({
@@ -9,7 +9,7 @@ export const deleteWorkflow = async ({
99
page: Page;
1010
workflowId: string;
1111
}) => {
12-
const { authToken } = await getAuthToken(page);
12+
const { authToken } = await getAccessAuthToken(page);
1313

1414
return page.request.post(backendGraphQLUrl, {
1515
headers: {

packages/twenty-e2e-testing/lib/requests/destroy-workflow.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Page } from '@playwright/test';
2-
import { getAuthToken } from '../utils/getAuthToken';
1+
import { type Page } from '@playwright/test';
2+
import { getAccessAuthToken } from '../utils/getAccessAuthToken';
33
import { backendGraphQLUrl } from './backend';
44

55
export const destroyWorkflow = async ({
@@ -9,7 +9,7 @@ export const destroyWorkflow = async ({
99
page: Page;
1010
workflowId: string;
1111
}) => {
12-
const { authToken } = await getAuthToken(page);
12+
const { authToken } = await getAccessAuthToken(page);
1313

1414
return page.request.post(backendGraphQLUrl, {
1515
headers: {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { type Page } from '@playwright/test';
2+
3+
const decodeToken = (cookie: any) =>
4+
JSON.parse(decodeURIComponent(cookie.value)).accessOrWorkspaceAgnosticToken
5+
?.token;
6+
7+
const decodePayload = (jwt: string) =>
8+
JSON.parse(Buffer.from(jwt.split('.')[1], 'base64url').toString());
9+
10+
11+
export const getAccessAuthToken = async (page: Page) => {
12+
const storageState = await page.context().storageState();
13+
const tokenCookies = storageState.cookies.filter(
14+
(cookie) => cookie.name === 'tokenPair',
15+
);
16+
if (!tokenCookies) {
17+
throw new Error('No auth cookie found');
18+
}
19+
const accessTokenCookie = tokenCookies.find(
20+
(cookie) => {
21+
const payload = decodePayload(decodeToken(cookie) ?? '');
22+
return payload.type === 'ACCESS';
23+
}
24+
);
25+
26+
const token = JSON.parse(decodeURIComponent(accessTokenCookie?.value ?? '')).accessOrWorkspaceAgnosticToken
27+
.token;
28+
29+
return { authToken: token };
30+
};

packages/twenty-e2e-testing/lib/utils/getAuthToken.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

packages/twenty-e2e-testing/playwright.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ export default defineConfig({
3434
expect: {
3535
timeout: 5000,
3636
},
37-
reporter: process.env.CI ? 'github' : 'list',
37+
reporter: [
38+
[process.env.CI ? 'github' : 'list'],
39+
['./reporters/log-summary-reporter.ts'],
40+
],
3841
projects: [
3942
{
4043
name: 'setup',
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type {
2+
Reporter,
3+
TestCase,
4+
TestResult,
5+
} from '@playwright/test/reporter';
6+
7+
class LogSummaryReporter implements Reporter {
8+
private passed: string[] = [];
9+
private failed: string[] = [];
10+
11+
onTestEnd(test: TestCase, result: TestResult): void {
12+
const name = test.titlePath().join(' › ');
13+
14+
if (result.status === 'passed') {
15+
this.passed.push(name);
16+
return;
17+
}
18+
19+
if (result.status === 'failed' || result.status === 'timedOut') {
20+
this.failed.push(name);
21+
}
22+
}
23+
24+
onEnd(): void {
25+
const passedSet = new Set(this.passed);
26+
const failedSet = new Set(this.failed);
27+
const flaky: string[] = [];
28+
29+
for (const testName of passedSet) {
30+
if (failedSet.has(testName)) {
31+
flaky.push(testName);
32+
}
33+
}
34+
35+
const uniquePassed = this.passed.filter((testName) => !flaky.includes(testName));
36+
const uniqueFailed = this.failed.filter((testName) => !flaky.includes(testName));
37+
38+
console.log('\n=== Playwright summary ===');
39+
if (uniquePassed.length) {
40+
console.log('Passed:');
41+
uniquePassed.forEach((testName) => console.log(` ✅ ${testName}`));
42+
}
43+
if (uniqueFailed.length) {
44+
console.log('Failed:');
45+
uniqueFailed.forEach((testName) => console.log(` ❌ ${testName}`));
46+
}
47+
if (flaky.length) {
48+
console.log('Flaky:');
49+
flaky.forEach((testName) => console.log(` ⚠️ ${testName}`));
50+
}
51+
}
52+
}
53+
54+
export default LogSummaryReporter;

0 commit comments

Comments
 (0)