Skip to content

Commit 6bbf31f

Browse files
committed
Merge remote-tracking branch 'foundation/main' into ci-desktop-regression-self-hosted
2 parents b97d8ae + f9d1f16 commit 6bbf31f

36 files changed

+2041
-143
lines changed

.env.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ SESSION_DESKTOP_ROOT="<path to session desktop root>"
22
PLAYWRIGHT_CUSTOM_REPORTER=1
33
PLAYWRIGHT_REPEAT_COUNT=0
44
PLAYWRIGHT_RETRIES_COUNT=0
5-
PLAYWRIGHT_WORKER_COUNT=1
5+
PLAYWRIGHT_WORKERS_COUNT=1

.eslintrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ module.exports = {
77
},
88
},
99

10-
extends: ['airbnb-base', 'prettier', 'plugin:@typescript-eslint/recommended'],
10+
extends: ['airbnb-base', 'airbnb-typescript/base', 'prettier', 'plugin:@typescript-eslint/recommended'],
1111

1212
plugins: ['mocha', 'more', '@typescript-eslint'],
1313
parser: '@typescript-eslint/parser',
14-
parserOptions: { project: ['tsconfig.json'] },
14+
parserOptions: { project: ['./tsconfig.json'] },
1515

1616
rules: {
1717
'comma-dangle': [

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ test-results/
33
/playwright-report/
44
/playwright/.cache/
55
**/*.js
6+
/*.js
7+
*.js
8+
*.js.map
69
.vscode/settings.json
710
tests/automation/todo
811
allure-report

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Create your own config from the `.env.sample` and edit the values to match your
4242
- *type*: number
4343
- *default*: 0
4444
- *description*: the number of retries each test. i.e. if a test **failed** on attempt x, and our current attempt is `< PLAYWRIGHT_RETRIES_COUNT` the test will be scheduled to be run again. This can be used to debug flaky tests
45-
- `PLAYWRIGHT_WORKER_COUNT`
45+
- `PLAYWRIGHT_WORKERS_COUNT`
4646
- *type*: number
4747
- *default*: 1
4848
- *description*: the number of workers to start in parallel. The more, the faster the test suite is going to run, but if you hit your CPU limit they'll start to be unnecessarily flaky. Should be fine with a value between 10-20 depending on the machine.

global.setup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export default async function globalSetup() {
3737
? join(homedir(), '.config')
3838
: null;
3939
if (!parentFolderOfAllDataPath) {
40-
throw new Error('Only macOS/linux are currently supported ');
40+
throw new Error('Only macOS/linux are currently supported');
4141
}
4242

4343
if (!parentFolderOfAllDataPath || parentFolderOfAllDataPath.length < 9) {

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"electron": "^25.4.0",
2020
"eslint": "^8.46.0",
2121
"eslint-config-airbnb-base": "^15.0.0",
22+
"eslint-config-airbnb-typescript": "^18.0.0",
2223
"eslint-config-prettier": "^9.0.0",
2324
"eslint-plugin-import": "^2.28.0",
2425
"eslint-plugin-mocha": "^10.1.0",
@@ -32,6 +33,7 @@
3233
},
3334
"scripts": {
3435
"lint": "yarn prettier . --write && yarn eslint .",
36+
"dev": "yarn tsc --watch",
3537
"test": "npx playwright test",
3638
"watch": "tsc -w",
3739
"allure-create": "allure generate ./allure-results",

playwright.config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import { defineConfig } from '@playwright/test';
44
import { isEmpty, toNumber } from 'lodash';
55

6-
import 'dotenv/config';
6+
import dotenv from 'dotenv';
7+
8+
dotenv.config();
79

810
const useSessionReporter = !isEmpty(process.env.PLAYWRIGHT_CUSTOM_REPORTER);
911

@@ -23,7 +25,7 @@ export default defineConfig({
2325
repeatEach: process.env.PLAYWRIGHT_REPEAT_COUNT
2426
? toNumber(process.env.PLAYWRIGHT_REPEAT_COUNT)
2527
: 0,
26-
workers: toNumber(process.env.PLAYWRIGHT_WORKER_COUNT) || 1,
28+
workers: toNumber(process.env.PLAYWRIGHT_WORKERS_COUNT) || 1,
2729
reportSlowTests: null,
2830
fullyParallel: true, // otherwise, tests in the same file are not run in parallel
2931
globalSetup: './global.setup', // clean leftovers of previous test runs on start, runs only once

sessionReporter.ts

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
TestResult,
99
} from '@playwright/test/reporter';
1010
import chalk from 'chalk';
11-
import { Dictionary, groupBy, mean, sortBy } from 'lodash';
11+
import { Dictionary, groupBy, isString, mean, sortBy } from 'lodash';
1212

1313
type TestAndResult = { test: TestCase; result: TestResult };
1414

@@ -21,6 +21,8 @@ function getChalkColorForStatus(result: Pick<TestResult, 'status'>) {
2121
? chalk.green
2222
: result.status === 'interrupted'
2323
? chalk.yellow
24+
: result.status === 'skipped'
25+
? chalk.blue
2426
: chalk.red;
2527
}
2628

@@ -33,7 +35,8 @@ function testResultToDurationStr(tests: Array<Pick<TestAndResult, 'result'>>) {
3335

3436
function formatGroupedByResults(testAndResults: Array<TestAndResult>) {
3537
const allPassed = testAndResults.every((m) => m.result.status === 'passed');
36-
const allFailed = testAndResults.every((m) => m.result.status !== 'passed');
38+
const allFailed = testAndResults.every((m) => m.result.status === 'failed');
39+
const allSkipped = testAndResults.every((m) => m.result.status === 'skipped');
3740
const firstItem = testAndResults[0]; // we know they all have the same state
3841
const statuses = testAndResults.map((m) => `"${m.result.status}"`).join(',');
3942

@@ -49,6 +52,8 @@ function formatGroupedByResults(testAndResults: Array<TestAndResult>) {
4952
? { status: 'passed' }
5053
: allFailed
5154
? { status: 'failed' }
55+
: allSkipped
56+
? { status: 'skipped' }
5257
: { status: 'interrupted' },
5358
)(
5459
`\t\t\t"${
@@ -60,18 +65,24 @@ function formatGroupedByResults(testAndResults: Array<TestAndResult>) {
6065
);
6166
}
6267

68+
function printOngoingTestLogs() {
69+
return process.env.PRINT_ONGOING_TEST_LOGS === '1';
70+
}
71+
72+
function printFailedTestLogs() {
73+
return process.env.PRINT_FAILED_TEST_LOGS === '1';
74+
}
75+
6376
class SessionReporter implements Reporter {
64-
private printTestConsole = false;
65-
private startTime: number = 0;
66-
private allTestsCount: number = 0;
77+
private startTime = 0;
78+
private allTestsCount = 0;
6779
private allResults: Array<TestAndResult> = [];
68-
private countWorkers: number = 1;
80+
private countWorkers = 1;
6981

7082
onBegin(config: FullConfig, suite: Suite) {
7183
this.allTestsCount = suite.allTests().length;
7284
this.countWorkers = config.workers;
7385

74-
this.printTestConsole = this.allTestsCount <= 1;
7586
console.log(
7687
`\t\tStarting the run with ${this.allTestsCount} tests, with ${this.countWorkers} workers, ${config.projects[0].retries} retries and ${config.projects[0].repeatEach} repeats`,
7788
);
@@ -94,11 +105,24 @@ class SessionReporter implements Reporter {
94105
`\t\tFinished test "${test.title}": ${result.status} with stdout/stderr`,
95106
)}`,
96107
);
97-
console.warn(`stdout:`);
98-
result.stdout.map((t) => process.stdout.write(t.toString()));
99-
100-
console.warn('stderr:');
101-
result.stderr.map((t) => process.stderr.write(t.toString()));
108+
if (printFailedTestLogs()) {
109+
console.info(`stdout:`);
110+
result.stdout.map((t) => process.stdout.write(t.toString()));
111+
112+
console.info('stderr:');
113+
result.stderr.map((t) => process.stderr.write(t.toString()));
114+
} else {
115+
console.info(
116+
`not printing stderr/stdout as PRINT_FAILED_TEST_LOGS is not '1'`,
117+
);
118+
}
119+
120+
const lastError = result.errors[result.errors.length - 1];
121+
console.info(
122+
`test failed with "${lastError?.message || 'unknown'}"\n\tsnippet:${
123+
lastError?.snippet || 'unknown'
124+
} \n\tstack:${lastError?.stack || 'unknown'}`,
125+
);
102126
} else {
103127
console.log(
104128
`${getChalkColorForStatus(result)(
@@ -108,7 +132,7 @@ class SessionReporter implements Reporter {
108132
}
109133
this.allResults.push({ test, result });
110134

111-
console.log(chalk.bgWhiteBright(`\t\tResults so far:`));
135+
console.log(chalk.bgWhiteBright.black(`\t\tResults so far:`));
112136
// we keep track of all the failed/passed states, but only render the passed status here even if it took a few retries
113137

114138
const { allFailedSoFar, allPassedSoFar, partiallyPassed } =
@@ -125,8 +149,8 @@ class SessionReporter implements Reporter {
125149
notPassedCount * mean(this.allResults.map((m) => m.result.duration));
126150
const estimatedTotalMins = Math.floor(estimateLeftMs / (60 * 1000));
127151
console.log(
128-
chalk.bgWhite(
129-
`\t\tRemaining tests: ${notPassedCount}, rougly ${estimatedTotalMins}min total left, so about ${Math.ceil(
152+
chalk.bgWhite.black(
153+
`\t\tRemaining tests: ${notPassedCount}, roughly ${estimatedTotalMins}min total left, so about ${Math.ceil(
130154
estimatedTotalMins / this.countWorkers,
131155
)}min as we have ${this.countWorkers} worker(s)...`,
132156
),
@@ -176,7 +200,7 @@ class SessionReporter implements Reporter {
176200

177201
onEnd(result: FullResult) {
178202
console.log(
179-
chalk.bgWhiteBright(
203+
chalk.bgWhiteBright.black(
180204
`\n\n\n\t\tFinished the run: ${result.status}, count of tests run: ${
181205
this.allResults.length
182206
}, took ${Math.floor(
@@ -197,15 +221,31 @@ class SessionReporter implements Reporter {
197221
test: void | TestCase,
198222
_result: void | TestResult,
199223
) {
200-
if (this.printTestConsole) {
224+
if (printOngoingTestLogs()) {
225+
process.stdout.write(
226+
`"${test ? `${chalk.cyanBright(test.title)}` : ''}": ${
227+
isString(chunk) ? chunk : chunk.toString('utf-8')
228+
}`,
229+
);
230+
}
231+
}
232+
233+
onStdErr?(
234+
chunk: string | Buffer,
235+
test: void | TestCase,
236+
_result: void | TestResult,
237+
) {
238+
if (printOngoingTestLogs()) {
201239
process.stdout.write(
202-
`"${test ? `${chalk.cyanBright(test.title)}` : ''}": ${chunk}`,
240+
`"${test ? `${chalk.cyanBright(test.title)}` : ''}":err: ${
241+
isString(chunk) ? chunk : chunk.toString('utf-8')
242+
}`,
203243
);
204244
}
205245
}
206246

207247
onError?(error: TestError) {
208-
console.warn('global error:', error);
248+
console.info('global error:', error);
209249
}
210250
}
211251

tests/automation/call_checks.spec.ts

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
11
import { englishStrippedStr } from '../locale/localizedString';
2-
import { sleepFor } from '../promise_utils';
32
import { test_Alice_1W_Bob_1W } from './setup/sessionTest';
43
import { createContact } from './utilities/create_contact';
5-
import { clickOnMatchingText, clickOnTestIdWithText } from './utilities/utils';
4+
import { waitForTestIdWithText } from './utilities/utils';
5+
import { makeVoiceCall } from './utilities/voice_call';
66

77
test_Alice_1W_Bob_1W(
88
'Voice calls',
99
async ({ alice, aliceWindow1, bob, bobWindow1 }) => {
1010
await createContact(aliceWindow1, bobWindow1, alice, bob);
11-
await clickOnTestIdWithText(aliceWindow1, 'call-button');
12-
await clickOnTestIdWithText(aliceWindow1, 'session-toast');
13-
await clickOnTestIdWithText(aliceWindow1, 'enable-calls');
14-
await clickOnTestIdWithText(aliceWindow1, 'session-confirm-ok-button');
15-
await clickOnTestIdWithText(aliceWindow1, 'message-section');
16-
await clickOnTestIdWithText(
17-
aliceWindow1,
18-
'module-conversation__user__profile-name',
19-
bob.userName,
20-
);
21-
await clickOnTestIdWithText(aliceWindow1, 'call-button');
22-
// Enable calls in window B
23-
await clickOnTestIdWithText(bobWindow1, 'session-toast');
24-
await clickOnTestIdWithText(bobWindow1, 'enable-calls');
25-
await clickOnTestIdWithText(bobWindow1, 'session-confirm-ok-button');
26-
await clickOnMatchingText(
11+
await makeVoiceCall(aliceWindow1, bobWindow1, alice, bob);
12+
// In the receivers window, the message is 'Call in progress'
13+
await waitForTestIdWithText(
2714
bobWindow1,
28-
englishStrippedStr('accept').toString(),
15+
'call-notification-answered-a-call',
16+
englishStrippedStr('callsInProgress').toString(),
17+
);
18+
// Control message should be '{callerName} called you'
19+
// await waitForTestIdWithText(
20+
// bobWindow1,
21+
// 'call-notification-answered-a-call',
22+
// englishStrippedStr('callsCalledYou')
23+
// .withArgs({ name: caller.userName })
24+
// .toString(),
25+
// );
26+
// In the callers window, the message is 'You called {reciverName}'
27+
await waitForTestIdWithText(
28+
aliceWindow1,
29+
'call-notification-started-call',
30+
englishStrippedStr('callsYouCalled')
31+
.withArgs({ name: bob.userName })
32+
.toString(),
2933
);
30-
await sleepFor(5000);
31-
await clickOnTestIdWithText(aliceWindow1, 'end-call');
3234
},
3335
);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { testCommunityName } from './constants/community';
2+
import { test_Alice_2W, test_Alice_2W_Bob_1W } from './setup/sessionTest';
3+
import { joinCommunity } from './utilities/join_community';
4+
import { sendMessage } from './utilities/message';
5+
import { replyTo } from './utilities/reply_message';
6+
import {
7+
clickOnTestIdWithText,
8+
typeIntoInput,
9+
waitForLoadingAnimationToFinish,
10+
} from './utilities/utils';
11+
12+
test_Alice_2W('Join community', async ({ aliceWindow1, aliceWindow2 }) => {
13+
await joinCommunity(aliceWindow1);
14+
await waitForLoadingAnimationToFinish(aliceWindow1, 'loading-spinner');
15+
await clickOnTestIdWithText(aliceWindow1, 'scroll-to-bottom-button');
16+
await sendMessage(aliceWindow1, 'Hello, community!');
17+
// Check linked device for community
18+
await clickOnTestIdWithText(
19+
aliceWindow2,
20+
'module-conversation__user__profile-name',
21+
testCommunityName,
22+
);
23+
});
24+
25+
test_Alice_2W_Bob_1W(
26+
'Send image to community',
27+
async ({ alice, bob, aliceWindow1, bobWindow1 }) => {
28+
const testMessage = 'Testing sending images to communities';
29+
const testImageMessage = `Image message + ${Date.now()}`;
30+
const testReply = `${bob.userName} replying to image from ${alice.userName}`;
31+
await Promise.all([joinCommunity(aliceWindow1), joinCommunity(bobWindow1)]);
32+
await Promise.all([
33+
waitForLoadingAnimationToFinish(aliceWindow1, 'loading-spinner'),
34+
waitForLoadingAnimationToFinish(bobWindow1, 'loading-spinner'),
35+
]);
36+
await Promise.all([
37+
clickOnTestIdWithText(aliceWindow1, 'scroll-to-bottom-button'),
38+
clickOnTestIdWithText(bobWindow1, 'scroll-to-bottom-button'),
39+
]);
40+
await sendMessage(aliceWindow1, testMessage);
41+
// Check linked device for community
42+
await clickOnTestIdWithText(
43+
aliceWindow1,
44+
'module-conversation__user__profile-name',
45+
testCommunityName,
46+
);
47+
await aliceWindow1.setInputFiles(
48+
"input[type='file']",
49+
'fixtures/test-image.png',
50+
);
51+
await typeIntoInput(
52+
aliceWindow1,
53+
'message-input-text-area',
54+
testImageMessage,
55+
);
56+
await clickOnTestIdWithText(aliceWindow1, 'send-message-button');
57+
await replyTo({
58+
senderWindow: bobWindow1,
59+
textMessage: testImageMessage,
60+
replyText: testReply,
61+
receiverWindow: aliceWindow1,
62+
});
63+
},
64+
);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const testCommunityLink =
2+
'https://chat.lokinet.dev/testing-all-the-things?public_key=1d7e7f92b1ed3643855c98ecac02fc7274033a3467653f047d6e433540c03f17';
3+
export const testCommunityName = 'Testing All The Things!';

0 commit comments

Comments
 (0)