Skip to content

Commit a14096f

Browse files
committed
Merge branch 'separateLog'
2 parents 4958c8e + 4761a4b commit a14096f

File tree

8 files changed

+89
-26
lines changed

8 files changed

+89
-26
lines changed

.github/workflows/playwright.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ jobs:
9494
SIMULATOR_PATH: ${{ github.workspace }}/simulator-bin/simulator
9595
run: make webe2etest
9696

97-
- name: Upload Playwright artifacts
98-
if: failure()
97+
- name: Upload Playwright artifacts and logs.
98+
if: always()
9999
uses: actions/upload-artifact@v4
100100
with:
101-
name: playwright-artifacts
101+
name: playwright-artifacts-and-logs
102102
path: frontends/web/test-results/*

frontends/web/tests/base.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import { expect } from '@playwright/test';
2020

2121
let servewallet: ServeWallet;
2222

23-
test('App main page loads', async ({ page, host, frontendPort, servewalletPort }) => {
23+
test('App main page loads', async ({ page, host, frontendPort, servewalletPort }, testInfo) => {
2424

2525
await test.step('Start servewallet', async () => {
26-
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host);
26+
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, testInfo.title, testInfo.project.name);
2727
await servewallet.start();
2828
});
2929

frontends/web/tests/gapSettings.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import { deleteAccountsFile } from './helpers/fs';
2424

2525
let servewallet: ServeWallet;
2626

27-
test('Gap limits are correctly saved', async ({ page, host, frontendPort, servewalletPort }) => {
27+
test('Gap limits are correctly saved', async ({ page, host, frontendPort, servewalletPort }, testInfo) => {
2828

2929
await test.step('Start servewallet', async () => {
30-
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host);
30+
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, testInfo.title, testInfo.project.name);
3131
await servewallet.start();
3232
});
3333

frontends/web/tests/helpers/fs.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,28 @@ function deleteFile(filePath: string) {
3434
} else {
3535
console.warn(`File ${filePath} does not exist, skipping removal.`);
3636
}
37+
38+
}
39+
40+
function sanitizeFileName(name: string): string {
41+
return name.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_-]/g, '');
42+
}
43+
44+
45+
/**
46+
* Returns a full path for a log file in test-results/<test>-<project>/
47+
* Automatically creates the folder if it doesn't exist.
48+
*
49+
* @param testName - Current test name
50+
* @param projectName - Playwright project name
51+
* @param logFileName - The log filename (e.g., 'backend.log' or 'simulator.log')
52+
*/
53+
export function getLogFilePath(testName: string, projectName: string, logFileName: string): string {
54+
const safeTest = sanitizeFileName(testName);
55+
const safeProject = sanitizeFileName(projectName);
56+
57+
const folderPath = path.resolve(process.cwd(), 'test-results', `${safeTest}-${safeProject}`);
58+
fs.mkdirSync(folderPath, { recursive: true });
59+
60+
return path.join(folderPath, logFileName);
3761
}

frontends/web/tests/helpers/servewallet.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
import { spawn, ChildProcess } from 'child_process';
1818
import * as net from 'net';
19+
import * as fs from 'fs';
1920
import type { Page } from '@playwright/test';
21+
import { getLogFilePath } from './fs';
2022

2123
async function connectOnce(host: string, port: number): Promise<void> {
2224
return new Promise((resolve, reject) => {
@@ -36,19 +38,25 @@ export interface ServeWalletOptions {
3638

3739
export class ServeWallet {
3840
private proc?: ChildProcess;
41+
private outStream?: number;
3942
private readonly simulator: boolean;
4043
private readonly page: Page;
4144
private readonly servewalletPort: number;
4245
private readonly frontendPort: number;
4346
private readonly host: string;
4447
private readonly timeout: number;
4548
private readonly testnet: boolean;
49+
private readonly testName: string;
50+
private readonly projectName: string;
51+
private readonly logPath: string;
4652

4753
constructor(
4854
page: Page,
4955
servewalletPort: number,
5056
frontendPort: number,
5157
host: string,
58+
testName: string,
59+
projectName: string,
5260
options: ServeWalletOptions = {}
5361
) {
5462
const { simulator = false, timeout = 90000, testnet = true } = options;
@@ -64,11 +72,24 @@ export class ServeWallet {
6472
this.simulator = simulator;
6573
this.timeout = timeout;
6674
this.testnet = testnet;
75+
this.testName = testName;
76+
this.projectName = projectName;
77+
78+
this.logPath = getLogFilePath(this.testName, this.projectName, 'servewallet.log');
79+
this.openOutStream(false); // On the first time, open the file in "w" mode.
80+
}
81+
82+
private openOutStream(append: boolean): void {
83+
if (this.outStream) {
84+
fs.closeSync(this.outStream);
85+
}
86+
this.outStream = fs.openSync(this.logPath, append ? 'a' : 'w');
6787
}
6888

6989
async start(): Promise<void> {
70-
let target: string;
90+
this.openOutStream(true); // On starts/restarts, open the file in "a" mode.
7191

92+
let target: string;
7293
if (this.testnet && !this.simulator) {
7394
target = 'servewallet';
7495
} else if (this.testnet && this.simulator) {
@@ -81,7 +102,7 @@ export class ServeWallet {
81102
}
82103

83104
this.proc = spawn('make', ['-C', '../../', target], {
84-
stdio: 'inherit',
105+
stdio: ['ignore', this.outStream, this.outStream],
85106
detached: true,
86107
});
87108

@@ -100,7 +121,7 @@ export class ServeWallet {
100121
);
101122
return;
102123
} catch {
103-
// page.goto failed, likely connection refused; retry
124+
// page.goto failed; likely connection refused; retry
104125
}
105126
} catch {
106127
// port not ready yet
@@ -124,6 +145,19 @@ export class ServeWallet {
124145
// Listen for exit event
125146
this.proc.once('exit', () => {
126147
console.log('Servewallet stopped');
148+
this.proc = undefined;
149+
150+
if (this.outStream) {
151+
try {
152+
fs.closeSync(this.outStream);
153+
console.log('Servewallet log file closed');
154+
} catch (err) {
155+
console.warn('Failed to close servewallet log file:', err);
156+
} finally {
157+
this.outStream = undefined;
158+
}
159+
}
160+
127161
resolve();
128162
});
129163

frontends/web/tests/helpers/simulator.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616

1717

18-
import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
18+
import { spawn, ChildProcess } from 'child_process';
1919
import type { Page } from '@playwright/test';
2020
import { clickButtonWithText, typeIntoFocusedInput, clickAllAgreements } from './dom';
21+
import { getLogFilePath } from './fs';
2122
import * as fs from 'fs';
2223
import * as path from 'path';
2324

@@ -55,30 +56,34 @@ export function cleanFakeMemoryFiles() {
5556
*/
5657
export function startSimulator(
5758
simulatorPath: string,
59+
testName: string,
60+
projectName: string,
5861
useFakeMemory = false
59-
): ChildProcessWithoutNullStreams {
62+
): ChildProcess {
6063
const env = { ...process.env };
6164
if (useFakeMemory) {
6265
env.FAKE_MEMORY_FILEPATH = '/tmp/fake_memory';
6366
}
6467

65-
const proc = spawn(simulatorPath, { stdio: 'pipe', env });
6668

67-
// Pipe output to logs (needed in CI)
68-
proc.stdout?.on('data', (chunk) => process.stdout.write(chunk));
69-
proc.stderr?.on('data', (chunk) => process.stderr.write(chunk));
69+
const logPath = getLogFilePath(testName, projectName, 'simulator.log');
70+
const outStream = fs.openSync(logPath, 'w');
71+
72+
const proc = spawn(simulatorPath, { stdio: ['ignore', outStream, outStream], env });
7073

7174
proc.on('error', (err) => {
7275
console.error('Simulator process error:', String(err));
7376
});
7477

7578
proc.on('exit', (code, signal) => {
7679
console.log(`Simulator exited: code=${String(code)}, signal=${String(signal)}`);
80+
fs.closeSync(outStream); // close log file when simulator exits
7781
});
7882

7983
return proc;
8084
}
8185

86+
8287
/**
8388
* Performs the wallet setup flow in order:
8489
* 1. Click "Continue"

frontends/web/tests/testnetRestart.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ let servewallet: ServeWallet;
3535
* - Kill servewallet
3636
* - Restart servewallet in mainnet mode - testnet mode should be disabled now.
3737
*/
38-
test('Testnet mode', async ({ page, host, frontendPort, servewalletPort }) => {
38+
test('Testnet mode', async ({ page, host, frontendPort, servewalletPort }, testInfo) => {
3939

4040
await test.step('Start servewallet', async () => {
41-
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, { testnet: false });
41+
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, testInfo.title, testInfo.project.name, { testnet: false });
4242
await servewallet.start();
4343
});
4444

frontends/web/tests/watch-only.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import { test } from './helpers/fixtures';
2020
import { ServeWallet } from './helpers/servewallet';
2121
import { startSimulator, completeWalletSetupFlow, cleanFakeMemoryFiles } from './helpers/simulator';
2222
import { assertFieldsCount, clickButtonWithText } from './helpers/dom';
23-
import { ChildProcessWithoutNullStreams } from 'child_process';
23+
import { ChildProcess } from 'child_process';
2424

2525
let servewallet: ServeWallet;
26-
let simulatorProc : ChildProcessWithoutNullStreams | undefined;
26+
let simulatorProc : ChildProcess | undefined;
2727

2828
/**
2929
* Test scenario 1:
@@ -34,9 +34,9 @@ let simulatorProc : ChildProcessWithoutNullStreams | undefined;
3434
* - Restart app (kill and restart servewallet)
3535
* - Check that accounts do not show up without simulator running.
3636
*/
37-
test('Test #1 - No passphrase and no watch-only', async ({ page, host, frontendPort, servewalletPort }) => {
37+
test('Test #1 - No passphrase and no watch-only', async ({ page, host, frontendPort, servewalletPort }, testInfo) => {
3838
await test.step('Start servewallet', async () => {
39-
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, { simulator: true });
39+
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, testInfo.title, testInfo.project.name, { simulator: true });
4040
await servewallet.start();
4141
});
4242

@@ -46,7 +46,7 @@ test('Test #1 - No passphrase and no watch-only', async ({ page, host, frontendP
4646
throw new Error('SIMULATOR_PATH environment variable not set');
4747
}
4848

49-
simulatorProc = startSimulator(simulatorPath, true);
49+
simulatorProc = startSimulator(simulatorPath, testInfo.title, testInfo.project.name, true);
5050
console.log('Simulator started');
5151
});
5252

@@ -88,9 +88,9 @@ test('Test #1 - No passphrase and no watch-only', async ({ page, host, frontendP
8888
* - Restart app (kill and restart servewallet)
8989
* - Check that watch-only accounts still show up (with no simulator running)
9090
*/
91-
test('Test #2 - No passphrase - Watch-only account', async ({ page, host, frontendPort, servewalletPort }) => {
91+
test('Test #2 - No passphrase - Watch-only account', async ({ page, host, frontendPort, servewalletPort }, testInfo) => {
9292
await test.step('Start servewallet', async () => {
93-
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, { simulator: true });
93+
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, testInfo.title, testInfo.project.name, { simulator: true });
9494
await servewallet.start();
9595
});
9696

@@ -101,7 +101,7 @@ test('Test #2 - No passphrase - Watch-only account', async ({ page, host, fronte
101101
throw new Error('SIMULATOR_PATH environment variable not set');
102102
}
103103

104-
simulatorProc = startSimulator(simulatorPath, true);
104+
simulatorProc = startSimulator(simulatorPath, testInfo.title, testInfo.project.name, true);
105105

106106
console.log('Simulator started');
107107
});

0 commit comments

Comments
 (0)