Better logging for hanging benchmarks
This commit is contained in:
parent
adf2957537
commit
51c2029b5c
9 changed files with 446 additions and 458 deletions
|
@ -291,7 +291,7 @@
|
||||||
"npm-run-all": "4.1.5",
|
"npm-run-all": "4.1.5",
|
||||||
"nyc": "11.4.1",
|
"nyc": "11.4.1",
|
||||||
"patch-package": "6.4.7",
|
"patch-package": "6.4.7",
|
||||||
"playwright": "1.30.0",
|
"playwright": "1.31.2",
|
||||||
"prettier": "2.8.0",
|
"prettier": "2.8.0",
|
||||||
"sass": "1.49.7",
|
"sass": "1.49.7",
|
||||||
"sass-loader": "10.2.0",
|
"sass-loader": "10.2.0",
|
||||||
|
|
|
@ -5,103 +5,87 @@
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import type { PrimaryDevice } from '@signalapp/mock-server';
|
import type { PrimaryDevice } from '@signalapp/mock-server';
|
||||||
|
|
||||||
import type { App } from './fixtures';
|
|
||||||
import { Bootstrap, debug, stats, RUN_COUNT, DISCARD_COUNT } from './fixtures';
|
import { Bootstrap, debug, stats, RUN_COUNT, DISCARD_COUNT } from './fixtures';
|
||||||
|
|
||||||
const CONVERSATION_SIZE = 1000; // messages
|
const CONVERSATION_SIZE = 1000; // messages
|
||||||
const DELAY = 50; // milliseconds
|
const DELAY = 50; // milliseconds
|
||||||
|
|
||||||
void (async () => {
|
Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
||||||
const bootstrap = new Bootstrap({
|
const app = await bootstrap.link();
|
||||||
benchmark: true,
|
const { server, contacts, phone, desktop } = bootstrap;
|
||||||
});
|
|
||||||
|
|
||||||
await bootstrap.init();
|
const [first, second] = contacts;
|
||||||
|
|
||||||
let app: App | undefined;
|
const messages = new Array<Buffer>();
|
||||||
try {
|
debug('encrypting');
|
||||||
app = await bootstrap.link();
|
// Send messages from just two contacts
|
||||||
const { server, contacts, phone, desktop } = bootstrap;
|
for (const contact of [second, first]) {
|
||||||
|
for (let i = 0; i < CONVERSATION_SIZE; i += 1) {
|
||||||
|
const messageTimestamp = bootstrap.getTimestamp();
|
||||||
|
messages.push(
|
||||||
|
await contact.encryptText(
|
||||||
|
desktop,
|
||||||
|
`hello from: ${contact.profileName}`,
|
||||||
|
{
|
||||||
|
timestamp: messageTimestamp,
|
||||||
|
sealed: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const [first, second] = contacts;
|
messages.push(
|
||||||
|
await phone.encryptSyncRead(desktop, {
|
||||||
const messages = new Array<Buffer>();
|
timestamp: bootstrap.getTimestamp(),
|
||||||
debug('encrypting');
|
messages: [
|
||||||
// Send messages from just two contacts
|
|
||||||
for (const contact of [second, first]) {
|
|
||||||
for (let i = 0; i < CONVERSATION_SIZE; i += 1) {
|
|
||||||
const messageTimestamp = bootstrap.getTimestamp();
|
|
||||||
messages.push(
|
|
||||||
await contact.encryptText(
|
|
||||||
desktop,
|
|
||||||
`hello from: ${contact.profileName}`,
|
|
||||||
{
|
{
|
||||||
|
senderUUID: contact.device.uuid,
|
||||||
timestamp: messageTimestamp,
|
timestamp: messageTimestamp,
|
||||||
sealed: true,
|
},
|
||||||
}
|
],
|
||||||
)
|
})
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
messages.push(
|
const sendQueue = async (): Promise<void> => {
|
||||||
await phone.encryptSyncRead(desktop, {
|
await Promise.all(messages.map(message => server.send(desktop, message)));
|
||||||
timestamp: bootstrap.getTimestamp(),
|
};
|
||||||
messages: [
|
|
||||||
{
|
const measure = async (): Promise<void> => {
|
||||||
senderUUID: contact.device.uuid,
|
assert(app);
|
||||||
timestamp: messageTimestamp,
|
const window = await app.getWindow();
|
||||||
},
|
|
||||||
],
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
})
|
|
||||||
);
|
const openConvo = async (contact: PrimaryDevice): Promise<void> => {
|
||||||
|
debug('opening conversation', contact.profileName);
|
||||||
|
const item = leftPane.locator(
|
||||||
|
`[data-testid="${contact.toContact().uuid}"]`
|
||||||
|
);
|
||||||
|
|
||||||
|
await item.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const deltaList = new Array<number>();
|
||||||
|
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
||||||
|
await openConvo(runId % 2 === 0 ? first : second);
|
||||||
|
|
||||||
|
debug('waiting for timing from the app');
|
||||||
|
const { delta } = await app.waitForConversationOpen();
|
||||||
|
|
||||||
|
// Let render complete
|
||||||
|
await new Promise(resolve => setTimeout(resolve, DELAY));
|
||||||
|
|
||||||
|
if (runId >= DISCARD_COUNT) {
|
||||||
|
deltaList.push(delta);
|
||||||
|
console.log('run=%d info=%j', runId - DISCARD_COUNT, { delta });
|
||||||
|
} else {
|
||||||
|
console.log('discarded=%d info=%j', runId, { delta });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendQueue = async (): Promise<void> => {
|
console.log('stats info=%j', { delta: stats(deltaList, [99, 99.8]) });
|
||||||
await Promise.all(messages.map(message => server.send(desktop, message)));
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const measure = async (): Promise<void> => {
|
await Promise.all([sendQueue(), measure()]);
|
||||||
assert(app);
|
});
|
||||||
const window = await app.getWindow();
|
|
||||||
|
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
|
||||||
|
|
||||||
const openConvo = async (contact: PrimaryDevice): Promise<void> => {
|
|
||||||
debug('opening conversation', contact.profileName);
|
|
||||||
const item = leftPane.locator(
|
|
||||||
`[data-testid="${contact.toContact().uuid}"]`
|
|
||||||
);
|
|
||||||
|
|
||||||
await item.click();
|
|
||||||
};
|
|
||||||
|
|
||||||
const deltaList = new Array<number>();
|
|
||||||
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
|
||||||
await openConvo(runId % 2 === 0 ? first : second);
|
|
||||||
|
|
||||||
debug('waiting for timing from the app');
|
|
||||||
const { delta } = await app.waitForConversationOpen();
|
|
||||||
|
|
||||||
// Let render complete
|
|
||||||
await new Promise(resolve => setTimeout(resolve, DELAY));
|
|
||||||
|
|
||||||
if (runId >= DISCARD_COUNT) {
|
|
||||||
deltaList.push(delta);
|
|
||||||
console.log('run=%d info=%j', runId - DISCARD_COUNT, { delta });
|
|
||||||
} else {
|
|
||||||
console.log('discarded=%d info=%j', runId, { delta });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('stats info=%j', { delta: stats(deltaList, [99, 99.8]) });
|
|
||||||
};
|
|
||||||
|
|
||||||
await Promise.all([sendQueue(), measure()]);
|
|
||||||
} catch (error) {
|
|
||||||
await bootstrap.saveLogs(app);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
await app?.close();
|
|
||||||
await bootstrap.teardown();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
ReceiptType,
|
ReceiptType,
|
||||||
} from '@signalapp/mock-server';
|
} from '@signalapp/mock-server';
|
||||||
|
|
||||||
import type { App } from './fixtures';
|
|
||||||
import {
|
import {
|
||||||
Bootstrap,
|
Bootstrap,
|
||||||
debug,
|
debug,
|
||||||
|
@ -23,13 +22,7 @@ import {
|
||||||
const CONVERSATION_SIZE = 500; // messages
|
const CONVERSATION_SIZE = 500; // messages
|
||||||
const LAST_MESSAGE = 'start sending messages now';
|
const LAST_MESSAGE = 'start sending messages now';
|
||||||
|
|
||||||
void (async () => {
|
Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
||||||
const bootstrap = new Bootstrap({
|
|
||||||
benchmark: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await bootstrap.init();
|
|
||||||
|
|
||||||
const { contacts, phone } = bootstrap;
|
const { contacts, phone } = bootstrap;
|
||||||
|
|
||||||
const members = [...contacts].slice(0, GROUP_SIZE);
|
const members = [...contacts].slice(0, GROUP_SIZE);
|
||||||
|
@ -45,145 +38,131 @@ void (async () => {
|
||||||
.pinGroup(group)
|
.pinGroup(group)
|
||||||
);
|
);
|
||||||
|
|
||||||
let app: App | undefined;
|
const app = await bootstrap.link();
|
||||||
|
|
||||||
try {
|
const { server, desktop } = bootstrap;
|
||||||
app = await bootstrap.link();
|
const [first] = members;
|
||||||
|
|
||||||
const { server, desktop } = bootstrap;
|
const messages = new Array<Buffer>();
|
||||||
const [first] = members;
|
debug('encrypting');
|
||||||
|
// Fill left pane
|
||||||
|
for (const contact of members.slice().reverse()) {
|
||||||
|
const messageTimestamp = bootstrap.getTimestamp();
|
||||||
|
|
||||||
const messages = new Array<Buffer>();
|
messages.push(
|
||||||
debug('encrypting');
|
await contact.encryptText(desktop, `hello from: ${contact.profileName}`, {
|
||||||
// Fill left pane
|
timestamp: messageTimestamp,
|
||||||
for (const contact of members.slice().reverse()) {
|
sealed: true,
|
||||||
const messageTimestamp = bootstrap.getTimestamp();
|
})
|
||||||
|
);
|
||||||
messages.push(
|
messages.push(
|
||||||
await contact.encryptText(
|
await phone.encryptSyncRead(desktop, {
|
||||||
desktop,
|
timestamp: bootstrap.getTimestamp(),
|
||||||
`hello from: ${contact.profileName}`,
|
messages: [
|
||||||
{
|
{
|
||||||
|
senderUUID: contact.device.uuid,
|
||||||
timestamp: messageTimestamp,
|
timestamp: messageTimestamp,
|
||||||
sealed: true,
|
},
|
||||||
}
|
],
|
||||||
)
|
})
|
||||||
);
|
);
|
||||||
messages.push(
|
}
|
||||||
await phone.encryptSyncRead(desktop, {
|
|
||||||
timestamp: bootstrap.getTimestamp(),
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
senderUUID: contact.device.uuid,
|
|
||||||
timestamp: messageTimestamp,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill group
|
// Fill group
|
||||||
for (let i = 0; i < CONVERSATION_SIZE; i += 1) {
|
for (let i = 0; i < CONVERSATION_SIZE; i += 1) {
|
||||||
const contact = members[i % members.length];
|
const contact = members[i % members.length];
|
||||||
const messageTimestamp = bootstrap.getTimestamp();
|
const messageTimestamp = bootstrap.getTimestamp();
|
||||||
|
|
||||||
const isLast = i === CONVERSATION_SIZE - 1;
|
const isLast = i === CONVERSATION_SIZE - 1;
|
||||||
|
|
||||||
messages.push(
|
messages.push(
|
||||||
await contact.encryptText(
|
await contact.encryptText(
|
||||||
desktop,
|
desktop,
|
||||||
isLast ? LAST_MESSAGE : `#${i} from: ${contact.profileName}`,
|
isLast ? LAST_MESSAGE : `#${i} from: ${contact.profileName}`,
|
||||||
|
{
|
||||||
|
timestamp: messageTimestamp,
|
||||||
|
sealed: true,
|
||||||
|
group,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
messages.push(
|
||||||
|
await phone.encryptSyncRead(desktop, {
|
||||||
|
timestamp: bootstrap.getTimestamp(),
|
||||||
|
messages: [
|
||||||
{
|
{
|
||||||
|
senderUUID: contact.device.uuid,
|
||||||
timestamp: messageTimestamp,
|
timestamp: messageTimestamp,
|
||||||
sealed: true,
|
},
|
||||||
group,
|
],
|
||||||
}
|
})
|
||||||
)
|
);
|
||||||
);
|
}
|
||||||
messages.push(
|
debug('encrypted');
|
||||||
await phone.encryptSyncRead(desktop, {
|
|
||||||
timestamp: bootstrap.getTimestamp(),
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
senderUUID: contact.device.uuid,
|
|
||||||
timestamp: messageTimestamp,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
debug('encrypted');
|
|
||||||
|
|
||||||
await Promise.all(messages.map(message => server.send(desktop, message)));
|
await Promise.all(messages.map(message => server.send(desktop, message)));
|
||||||
|
|
||||||
const window = await app.getWindow();
|
const window = await app.getWindow();
|
||||||
|
|
||||||
debug('opening conversation');
|
debug('opening conversation');
|
||||||
{
|
{
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
const item = leftPane
|
const item = leftPane
|
||||||
.locator(
|
.locator(
|
||||||
'.module-conversation-list__item--contact-or-conversation' +
|
'.module-conversation-list__item--contact-or-conversation' +
|
||||||
`>> text=${LAST_MESSAGE}`
|
`>> text=${LAST_MESSAGE}`
|
||||||
)
|
)
|
||||||
.first();
|
.first();
|
||||||
await item.click();
|
await item.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeline = window.locator(
|
const timeline = window.locator(
|
||||||
'.timeline-wrapper, .conversation .ConversationView'
|
'.timeline-wrapper, .conversation .ConversationView'
|
||||||
|
);
|
||||||
|
|
||||||
|
const deltaList = new Array<number>();
|
||||||
|
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
||||||
|
debug('finding composition input and clicking it');
|
||||||
|
const composeArea = window.locator(
|
||||||
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
|
|
||||||
const deltaList = new Array<number>();
|
const input = composeArea.locator('[data-testid=CompositionInput]');
|
||||||
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
|
||||||
debug('finding composition input and clicking it');
|
|
||||||
const composeArea = window.locator(
|
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
|
||||||
);
|
|
||||||
|
|
||||||
const input = composeArea.locator('[data-testid=CompositionInput]');
|
debug('entering message text');
|
||||||
|
await input.type(`my message ${runId}`);
|
||||||
|
await input.press('Enter');
|
||||||
|
|
||||||
debug('entering message text');
|
debug('waiting for message on server side');
|
||||||
await input.type(`my message ${runId}`);
|
const { body, source, envelopeType } = await first.waitForMessage();
|
||||||
await input.press('Enter');
|
assert.strictEqual(body, `my message ${runId}`);
|
||||||
|
assert.strictEqual(source, desktop);
|
||||||
|
assert.strictEqual(envelopeType, EnvelopeType.SenderKey);
|
||||||
|
|
||||||
debug('waiting for message on server side');
|
debug('waiting for timing from the app');
|
||||||
const { body, source, envelopeType } = await first.waitForMessage();
|
const { timestamp, delta } = await app.waitForMessageSend();
|
||||||
assert.strictEqual(body, `my message ${runId}`);
|
|
||||||
assert.strictEqual(source, desktop);
|
|
||||||
assert.strictEqual(envelopeType, EnvelopeType.SenderKey);
|
|
||||||
|
|
||||||
debug('waiting for timing from the app');
|
debug('sending delivery receipts');
|
||||||
const { timestamp, delta } = await app.waitForMessageSend();
|
const delivery = await first.encryptReceipt(desktop, {
|
||||||
|
timestamp: timestamp + 1,
|
||||||
|
messageTimestamps: [timestamp],
|
||||||
|
type: ReceiptType.Delivery,
|
||||||
|
});
|
||||||
|
|
||||||
debug('sending delivery receipts');
|
await server.send(desktop, delivery);
|
||||||
const delivery = await first.encryptReceipt(desktop, {
|
|
||||||
timestamp: timestamp + 1,
|
|
||||||
messageTimestamps: [timestamp],
|
|
||||||
type: ReceiptType.Delivery,
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.send(desktop, delivery);
|
debug('waiting for message state change');
|
||||||
|
const message = timeline.locator(`[data-testid="${timestamp}"]`);
|
||||||
|
await message.waitFor();
|
||||||
|
|
||||||
debug('waiting for message state change');
|
if (runId >= DISCARD_COUNT) {
|
||||||
const message = timeline.locator(`[data-testid="${timestamp}"]`);
|
deltaList.push(delta);
|
||||||
await message.waitFor();
|
console.log('run=%d info=%j', runId - DISCARD_COUNT, { delta });
|
||||||
|
} else {
|
||||||
if (runId >= DISCARD_COUNT) {
|
console.log('discarded=%d info=%j', runId, { delta });
|
||||||
deltaList.push(delta);
|
|
||||||
console.log('run=%d info=%j', runId - DISCARD_COUNT, { delta });
|
|
||||||
} else {
|
|
||||||
console.log('discarded=%d info=%j', runId, { delta });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('stats info=%j', { delta: stats(deltaList, [99, 99.8]) });
|
|
||||||
} catch (error) {
|
|
||||||
await bootstrap.saveLogs(app);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
await app?.close();
|
|
||||||
await bootstrap.teardown();
|
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
console.log('stats info=%j', { delta: stats(deltaList, [99, 99.8]) });
|
||||||
|
});
|
||||||
|
|
|
@ -6,131 +6,115 @@ import assert from 'assert';
|
||||||
|
|
||||||
import { ReceiptType } from '@signalapp/mock-server';
|
import { ReceiptType } from '@signalapp/mock-server';
|
||||||
|
|
||||||
import type { App } from './fixtures';
|
|
||||||
import { Bootstrap, debug, stats, RUN_COUNT, DISCARD_COUNT } from './fixtures';
|
import { Bootstrap, debug, stats, RUN_COUNT, DISCARD_COUNT } from './fixtures';
|
||||||
|
|
||||||
const CONVERSATION_SIZE = 500; // messages
|
const CONVERSATION_SIZE = 500; // messages
|
||||||
|
|
||||||
const LAST_MESSAGE = 'start sending messages now';
|
const LAST_MESSAGE = 'start sending messages now';
|
||||||
|
|
||||||
void (async () => {
|
Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
||||||
const bootstrap = new Bootstrap({
|
const app = await bootstrap.link();
|
||||||
benchmark: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await bootstrap.init();
|
const { server, contacts, phone, desktop } = bootstrap;
|
||||||
let app: App | undefined;
|
|
||||||
|
|
||||||
try {
|
const [first] = contacts;
|
||||||
app = await bootstrap.link();
|
|
||||||
|
|
||||||
const { server, contacts, phone, desktop } = bootstrap;
|
const messages = new Array<Buffer>();
|
||||||
|
debug('encrypting');
|
||||||
|
// Note: make it so that we receive the latest message from the first
|
||||||
|
// contact.
|
||||||
|
for (const contact of contacts.slice().reverse()) {
|
||||||
|
let count = 1;
|
||||||
|
if (contact === first) {
|
||||||
|
count = CONVERSATION_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
const [first] = contacts;
|
for (let i = 0; i < count; i += 1) {
|
||||||
|
const messageTimestamp = bootstrap.getTimestamp();
|
||||||
|
|
||||||
const messages = new Array<Buffer>();
|
const isLast = i === count - 1;
|
||||||
debug('encrypting');
|
|
||||||
// Note: make it so that we receive the latest message from the first
|
|
||||||
// contact.
|
|
||||||
for (const contact of contacts.slice().reverse()) {
|
|
||||||
let count = 1;
|
|
||||||
if (contact === first) {
|
|
||||||
count = CONVERSATION_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < count; i += 1) {
|
messages.push(
|
||||||
const messageTimestamp = bootstrap.getTimestamp();
|
await contact.encryptText(
|
||||||
|
desktop,
|
||||||
const isLast = i === count - 1;
|
isLast ? LAST_MESSAGE : `#${i} from: ${contact.profileName}`,
|
||||||
|
{
|
||||||
messages.push(
|
timestamp: messageTimestamp,
|
||||||
await contact.encryptText(
|
sealed: true,
|
||||||
desktop,
|
}
|
||||||
isLast ? LAST_MESSAGE : `#${i} from: ${contact.profileName}`,
|
)
|
||||||
|
);
|
||||||
|
messages.push(
|
||||||
|
await phone.encryptSyncRead(desktop, {
|
||||||
|
timestamp: bootstrap.getTimestamp(),
|
||||||
|
messages: [
|
||||||
{
|
{
|
||||||
|
senderUUID: contact.device.uuid,
|
||||||
timestamp: messageTimestamp,
|
timestamp: messageTimestamp,
|
||||||
sealed: true,
|
},
|
||||||
}
|
],
|
||||||
)
|
})
|
||||||
);
|
|
||||||
messages.push(
|
|
||||||
await phone.encryptSyncRead(desktop, {
|
|
||||||
timestamp: bootstrap.getTimestamp(),
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
senderUUID: contact.device.uuid,
|
|
||||||
timestamp: messageTimestamp,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(messages.map(message => server.send(desktop, message)));
|
|
||||||
|
|
||||||
const window = await app.getWindow();
|
|
||||||
|
|
||||||
debug('opening conversation');
|
|
||||||
{
|
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
|
||||||
const item = leftPane.locator(
|
|
||||||
`[data-testid="${first.toContact().uuid}"] >> text=${LAST_MESSAGE}`
|
|
||||||
);
|
);
|
||||||
await item.click();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeline = window.locator(
|
|
||||||
'.timeline-wrapper, .conversation .ConversationView'
|
|
||||||
);
|
|
||||||
|
|
||||||
const deltaList = new Array<number>();
|
|
||||||
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
|
||||||
debug('finding composition input and clicking it');
|
|
||||||
const composeArea = window.locator(
|
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
|
||||||
);
|
|
||||||
const input = composeArea.locator('[data-testid=CompositionInput]');
|
|
||||||
|
|
||||||
debug('entering message text');
|
|
||||||
await input.type(`my message ${runId}`);
|
|
||||||
await input.press('Enter');
|
|
||||||
|
|
||||||
debug('waiting for message on server side');
|
|
||||||
const { body, source } = await first.waitForMessage();
|
|
||||||
assert.strictEqual(body, `my message ${runId}`);
|
|
||||||
assert.strictEqual(source, desktop);
|
|
||||||
|
|
||||||
debug('waiting for timing from the app');
|
|
||||||
const { timestamp, delta } = await app.waitForMessageSend();
|
|
||||||
|
|
||||||
debug('sending delivery receipt');
|
|
||||||
const delivery = await first.encryptReceipt(desktop, {
|
|
||||||
timestamp: timestamp + 1,
|
|
||||||
messageTimestamps: [timestamp],
|
|
||||||
type: ReceiptType.Delivery,
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.send(desktop, delivery);
|
|
||||||
|
|
||||||
debug('waiting for message state change');
|
|
||||||
const message = timeline.locator(`[data-testid="${timestamp}"]`);
|
|
||||||
await message.waitFor();
|
|
||||||
|
|
||||||
if (runId >= DISCARD_COUNT) {
|
|
||||||
deltaList.push(delta);
|
|
||||||
console.log('run=%d info=%j', runId - DISCARD_COUNT, { delta });
|
|
||||||
} else {
|
|
||||||
console.log('discarded=%d info=%j', runId, { delta });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('stats info=%j', { delta: stats(deltaList, [99, 99.8]) });
|
|
||||||
} catch (error) {
|
|
||||||
await bootstrap.saveLogs(app);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
await app?.close();
|
|
||||||
await bootstrap.teardown();
|
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
await Promise.all(messages.map(message => server.send(desktop, message)));
|
||||||
|
|
||||||
|
const window = await app.getWindow();
|
||||||
|
|
||||||
|
debug('opening conversation');
|
||||||
|
{
|
||||||
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
const item = leftPane.locator(
|
||||||
|
`[data-testid="${first.toContact().uuid}"] >> text=${LAST_MESSAGE}`
|
||||||
|
);
|
||||||
|
await item.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeline = window.locator(
|
||||||
|
'.timeline-wrapper, .conversation .ConversationView'
|
||||||
|
);
|
||||||
|
|
||||||
|
const deltaList = new Array<number>();
|
||||||
|
for (let runId = 0; runId < RUN_COUNT + DISCARD_COUNT; runId += 1) {
|
||||||
|
debug('finding composition input and clicking it');
|
||||||
|
const composeArea = window.locator(
|
||||||
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
|
);
|
||||||
|
const input = composeArea.locator('[data-testid=CompositionInput]');
|
||||||
|
|
||||||
|
debug('entering message text');
|
||||||
|
await input.type(`my message ${runId}`);
|
||||||
|
await input.press('Enter');
|
||||||
|
|
||||||
|
debug('waiting for message on server side');
|
||||||
|
const { body, source } = await first.waitForMessage();
|
||||||
|
assert.strictEqual(body, `my message ${runId}`);
|
||||||
|
assert.strictEqual(source, desktop);
|
||||||
|
|
||||||
|
debug('waiting for timing from the app');
|
||||||
|
const { timestamp, delta } = await app.waitForMessageSend();
|
||||||
|
|
||||||
|
debug('sending delivery receipt');
|
||||||
|
const delivery = await first.encryptReceipt(desktop, {
|
||||||
|
timestamp: timestamp + 1,
|
||||||
|
messageTimestamps: [timestamp],
|
||||||
|
type: ReceiptType.Delivery,
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.send(desktop, delivery);
|
||||||
|
|
||||||
|
debug('waiting for message state change');
|
||||||
|
const message = timeline.locator(`[data-testid="${timestamp}"]`);
|
||||||
|
await message.waitFor();
|
||||||
|
|
||||||
|
if (runId >= DISCARD_COUNT) {
|
||||||
|
deltaList.push(delta);
|
||||||
|
console.log('run=%d info=%j', runId - DISCARD_COUNT, { delta });
|
||||||
|
} else {
|
||||||
|
console.log('discarded=%d info=%j', runId, { delta });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('stats info=%j', { delta: stats(deltaList, [99, 99.8]) });
|
||||||
|
});
|
||||||
|
|
|
@ -10,127 +10,115 @@ const MESSAGE_BATCH_SIZE = 1000; // messages
|
||||||
|
|
||||||
const ENABLE_RECEIPTS = Boolean(process.env.ENABLE_RECEIPTS);
|
const ENABLE_RECEIPTS = Boolean(process.env.ENABLE_RECEIPTS);
|
||||||
|
|
||||||
void (async () => {
|
Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
||||||
const bootstrap = new Bootstrap({
|
|
||||||
benchmark: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await bootstrap.init();
|
|
||||||
await bootstrap.linkAndClose();
|
await bootstrap.linkAndClose();
|
||||||
|
|
||||||
try {
|
const { server, contacts, phone, desktop } = bootstrap;
|
||||||
const { server, contacts, phone, desktop } = bootstrap;
|
|
||||||
|
|
||||||
const messagesPerSec = new Array<number>();
|
const messagesPerSec = new Array<number>();
|
||||||
|
|
||||||
for (let runId = 0; runId < RUN_COUNT; runId += 1) {
|
for (let runId = 0; runId < RUN_COUNT; runId += 1) {
|
||||||
// Generate messages
|
// Generate messages
|
||||||
const messagePromises = new Array<Promise<Buffer>>();
|
const messagePromises = new Array<Promise<Buffer>>();
|
||||||
debug('started generating messages');
|
debug('started generating messages');
|
||||||
|
|
||||||
for (let i = 0; i < MESSAGE_BATCH_SIZE; i += 1) {
|
for (let i = 0; i < MESSAGE_BATCH_SIZE; i += 1) {
|
||||||
const contact = contacts[Math.floor(i / 2) % contacts.length];
|
const contact = contacts[Math.floor(i / 2) % contacts.length];
|
||||||
const direction = i % 2 ? 'message' : 'reply';
|
const direction = i % 2 ? 'message' : 'reply';
|
||||||
|
|
||||||
const messageTimestamp = bootstrap.getTimestamp();
|
const messageTimestamp = bootstrap.getTimestamp();
|
||||||
|
|
||||||
if (direction === 'message') {
|
|
||||||
messagePromises.push(
|
|
||||||
contact.encryptText(
|
|
||||||
desktop,
|
|
||||||
`Ping from mock server ${i + 1} / ${MESSAGE_BATCH_SIZE}`,
|
|
||||||
{
|
|
||||||
timestamp: messageTimestamp,
|
|
||||||
sealed: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ENABLE_RECEIPTS) {
|
|
||||||
messagePromises.push(
|
|
||||||
phone.encryptSyncRead(desktop, {
|
|
||||||
timestamp: bootstrap.getTimestamp(),
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
senderUUID: contact.device.uuid,
|
|
||||||
timestamp: messageTimestamp,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (direction === 'message') {
|
||||||
messagePromises.push(
|
messagePromises.push(
|
||||||
phone.encryptSyncSent(
|
contact.encryptText(
|
||||||
desktop,
|
desktop,
|
||||||
`Pong from mock server ${i + 1} / ${MESSAGE_BATCH_SIZE}`,
|
`Ping from mock server ${i + 1} / ${MESSAGE_BATCH_SIZE}`,
|
||||||
{
|
{
|
||||||
timestamp: messageTimestamp,
|
timestamp: messageTimestamp,
|
||||||
destinationUUID: contact.device.uuid,
|
sealed: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ENABLE_RECEIPTS) {
|
if (ENABLE_RECEIPTS) {
|
||||||
messagePromises.push(
|
messagePromises.push(
|
||||||
contact.encryptReceipt(desktop, {
|
phone.encryptSyncRead(desktop, {
|
||||||
timestamp: bootstrap.getTimestamp(),
|
timestamp: bootstrap.getTimestamp(),
|
||||||
messageTimestamps: [messageTimestamp],
|
messages: [
|
||||||
type: ReceiptType.Delivery,
|
{
|
||||||
})
|
senderUUID: contact.device.uuid,
|
||||||
);
|
timestamp: messageTimestamp,
|
||||||
messagePromises.push(
|
},
|
||||||
contact.encryptReceipt(desktop, {
|
],
|
||||||
timestamp: bootstrap.getTimestamp(),
|
|
||||||
messageTimestamps: [messageTimestamp],
|
|
||||||
type: ReceiptType.Read,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('ended generating messages');
|
messagePromises.push(
|
||||||
|
phone.encryptSyncSent(
|
||||||
|
desktop,
|
||||||
|
`Pong from mock server ${i + 1} / ${MESSAGE_BATCH_SIZE}`,
|
||||||
|
{
|
||||||
|
timestamp: messageTimestamp,
|
||||||
|
destinationUUID: contact.device.uuid,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const messages = await Promise.all(messagePromises);
|
if (ENABLE_RECEIPTS) {
|
||||||
|
messagePromises.push(
|
||||||
// Open the flood gates
|
contact.encryptReceipt(desktop, {
|
||||||
{
|
timestamp: bootstrap.getTimestamp(),
|
||||||
debug('got synced, sending messages');
|
messageTimestamps: [messageTimestamp],
|
||||||
|
type: ReceiptType.Delivery,
|
||||||
// Queue all messages
|
})
|
||||||
const queue = async (): Promise<void> => {
|
);
|
||||||
await Promise.all(
|
messagePromises.push(
|
||||||
messages.map(message => {
|
contact.encryptReceipt(desktop, {
|
||||||
return server.send(desktop, message);
|
timestamp: bootstrap.getTimestamp(),
|
||||||
})
|
messageTimestamps: [messageTimestamp],
|
||||||
);
|
type: ReceiptType.Read,
|
||||||
};
|
})
|
||||||
|
);
|
||||||
const run = async (): Promise<void> => {
|
|
||||||
const app = await bootstrap.startApp();
|
|
||||||
const appLoadedInfo = await app.waitUntilLoaded();
|
|
||||||
|
|
||||||
console.log('run=%d info=%j', runId, appLoadedInfo);
|
|
||||||
|
|
||||||
messagesPerSec.push(appLoadedInfo.messagesPerSec);
|
|
||||||
|
|
||||||
await app.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
await Promise.all([queue(), run()]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute human-readable statistics
|
debug('ended generating messages');
|
||||||
if (messagesPerSec.length !== 0) {
|
|
||||||
console.log('stats info=%j', { messagesPerSec: stats(messagesPerSec) });
|
const messages = await Promise.all(messagePromises);
|
||||||
|
|
||||||
|
// Open the flood gates
|
||||||
|
{
|
||||||
|
debug('got synced, sending messages');
|
||||||
|
|
||||||
|
// Queue all messages
|
||||||
|
const queue = async (): Promise<void> => {
|
||||||
|
await Promise.all(
|
||||||
|
messages.map(message => {
|
||||||
|
return server.send(desktop, message);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const run = async (): Promise<void> => {
|
||||||
|
const app = await bootstrap.startApp();
|
||||||
|
const appLoadedInfo = await app.waitUntilLoaded();
|
||||||
|
|
||||||
|
console.log('run=%d info=%j', runId, appLoadedInfo);
|
||||||
|
|
||||||
|
messagesPerSec.push(appLoadedInfo.messagesPerSec);
|
||||||
|
|
||||||
|
await app.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.all([queue(), run()]);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
await bootstrap.saveLogs();
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
await bootstrap.teardown();
|
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
// Compute human-readable statistics
|
||||||
|
if (messagesPerSec.length !== 0) {
|
||||||
|
console.log('stats info=%j', { messagesPerSec: stats(messagesPerSec) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -5,22 +5,16 @@
|
||||||
import type { PrimaryDevice } from '@signalapp/mock-server';
|
import type { PrimaryDevice } from '@signalapp/mock-server';
|
||||||
import { StorageState } from '@signalapp/mock-server';
|
import { StorageState } from '@signalapp/mock-server';
|
||||||
|
|
||||||
import type { App } from './fixtures';
|
|
||||||
import { Bootstrap } from './fixtures';
|
import { Bootstrap } from './fixtures';
|
||||||
|
|
||||||
const CONTACT_COUNT = 1000;
|
const CONTACT_COUNT = 1000;
|
||||||
|
|
||||||
void (async () => {
|
Bootstrap.benchmark(async (bootstrap: Bootstrap): Promise<void> => {
|
||||||
const contactNames = new Array<string>();
|
const contactNames = new Array<string>();
|
||||||
for (let i = 0; i < CONTACT_COUNT; i += 1) {
|
for (let i = 0; i < CONTACT_COUNT; i += 1) {
|
||||||
contactNames.push(`Contact ${i}`);
|
contactNames.push(`Contact ${i}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootstrap = new Bootstrap({
|
|
||||||
benchmark: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await bootstrap.init();
|
|
||||||
const { phone, server } = bootstrap;
|
const { phone, server } = bootstrap;
|
||||||
|
|
||||||
let state = StorageState.getEmpty();
|
let state = StorageState.getEmpty();
|
||||||
|
@ -50,25 +44,16 @@ void (async () => {
|
||||||
await phone.setStorageState(state);
|
await phone.setStorageState(state);
|
||||||
|
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
let app: App | undefined;
|
const app = await bootstrap.link();
|
||||||
try {
|
const window = await app.getWindow();
|
||||||
app = await bootstrap.link();
|
|
||||||
const window = await app.getWindow();
|
|
||||||
|
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
const item = leftPane.locator(
|
const item = leftPane.locator(
|
||||||
`[data-testid="${lastContact?.toContact().uuid}"]`
|
`[data-testid="${lastContact?.toContact().uuid}"]`
|
||||||
);
|
);
|
||||||
await item.waitFor();
|
await item.waitFor();
|
||||||
|
|
||||||
const duration = Date.now() - start;
|
const duration = Date.now() - start;
|
||||||
console.log(`Took: ${(duration / 1000).toFixed(2)} seconds`);
|
console.log(`Took: ${(duration / 1000).toFixed(2)} seconds`);
|
||||||
} catch (error) {
|
});
|
||||||
await bootstrap.saveLogs(app);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
await app?.close();
|
|
||||||
await bootstrap.teardown();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
|
@ -6,11 +6,13 @@ import fs from 'fs/promises';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import createDebug from 'debug';
|
import createDebug from 'debug';
|
||||||
|
import pTimeout from 'p-timeout';
|
||||||
|
|
||||||
import type { Device, PrimaryDevice } from '@signalapp/mock-server';
|
import type { Device, PrimaryDevice } from '@signalapp/mock-server';
|
||||||
import { Server, UUIDKind, loadCertificates } from '@signalapp/mock-server';
|
import { Server, UUIDKind, loadCertificates } from '@signalapp/mock-server';
|
||||||
import { MAX_READ_KEYS as MAX_STORAGE_READ_KEYS } from '../services/storageConstants';
|
import { MAX_READ_KEYS as MAX_STORAGE_READ_KEYS } from '../services/storageConstants';
|
||||||
import * as durations from '../util/durations';
|
import * as durations from '../util/durations';
|
||||||
|
import { drop } from '../util/drop';
|
||||||
import { App } from './playwright';
|
import { App } from './playwright';
|
||||||
|
|
||||||
export { App };
|
export { App };
|
||||||
|
@ -111,6 +113,7 @@ export class Bootstrap {
|
||||||
private privDesktop?: Device;
|
private privDesktop?: Device;
|
||||||
private storagePath?: string;
|
private storagePath?: string;
|
||||||
private timestamp: number = Date.now() - durations.WEEK;
|
private timestamp: number = Date.now() - durations.WEEK;
|
||||||
|
private lastApp?: App;
|
||||||
|
|
||||||
constructor(options: BootstrapOptions = {}) {
|
constructor(options: BootstrapOptions = {}) {
|
||||||
this.server = new Server({
|
this.server = new Server({
|
||||||
|
@ -178,6 +181,13 @@ export class Bootstrap {
|
||||||
debug('setting storage path=%j', this.storagePath);
|
debug('setting storage path=%j', this.storagePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static benchmark(
|
||||||
|
fn: (bootstrap: Bootstrap) => Promise<void>,
|
||||||
|
timeout = 5 * durations.MINUTE
|
||||||
|
): void {
|
||||||
|
drop(Bootstrap.runBenchmark(fn, timeout));
|
||||||
|
}
|
||||||
|
|
||||||
public get logsDir(): string {
|
public get logsDir(): string {
|
||||||
assert(
|
assert(
|
||||||
this.storagePath !== undefined,
|
this.storagePath !== undefined,
|
||||||
|
@ -191,10 +201,13 @@ export class Bootstrap {
|
||||||
debug('tearing down');
|
debug('tearing down');
|
||||||
|
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
this.storagePath
|
Promise.all([
|
||||||
? fs.rm(this.storagePath, { recursive: true })
|
this.storagePath
|
||||||
: Promise.resolve(),
|
? fs.rm(this.storagePath, { recursive: true })
|
||||||
this.server.close(),
|
: Promise.resolve(),
|
||||||
|
this.server.close(),
|
||||||
|
this.lastApp?.close(),
|
||||||
|
]),
|
||||||
new Promise(resolve => setTimeout(resolve, CLOSE_TIMEOUT).unref()),
|
new Promise(resolve => setTimeout(resolve, CLOSE_TIMEOUT).unref()),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -260,6 +273,13 @@ export class Bootstrap {
|
||||||
|
|
||||||
await app.start();
|
await app.start();
|
||||||
|
|
||||||
|
this.lastApp = app;
|
||||||
|
app.on('close', () => {
|
||||||
|
if (this.lastApp === app) {
|
||||||
|
this.lastApp = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +289,7 @@ export class Bootstrap {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveLogs(app?: App): Promise<void> {
|
public async saveLogs(app: App | undefined = this.lastApp): Promise<void> {
|
||||||
const { ARTIFACTS_DIR } = process.env;
|
const { ARTIFACTS_DIR } = process.env;
|
||||||
if (!ARTIFACTS_DIR) {
|
if (!ARTIFACTS_DIR) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
@ -334,6 +354,26 @@ export class Bootstrap {
|
||||||
// Private
|
// Private
|
||||||
//
|
//
|
||||||
|
|
||||||
|
private static async runBenchmark(
|
||||||
|
fn: (bootstrap: Bootstrap) => Promise<void>,
|
||||||
|
timeout: number
|
||||||
|
): Promise<void> {
|
||||||
|
const bootstrap = new Bootstrap({
|
||||||
|
benchmark: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await bootstrap.init();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pTimeout(fn(bootstrap), timeout);
|
||||||
|
} catch (error) {
|
||||||
|
await bootstrap.saveLogs();
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
await bootstrap.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async generateConfig(port: number): Promise<string> {
|
private async generateConfig(port: number): Promise<string> {
|
||||||
const url = `https://127.0.0.1:${port}`;
|
const url = `https://127.0.0.1:${port}`;
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import type { ElectronApplication, Page } from 'playwright';
|
import type { ElectronApplication, Page } from 'playwright';
|
||||||
import { _electron as electron } from 'playwright';
|
import { _electron as electron } from 'playwright';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
IPCRequest as ChallengeRequestType,
|
IPCRequest as ChallengeRequestType,
|
||||||
|
@ -39,10 +40,12 @@ export type AppOptionsType = Readonly<{
|
||||||
config: string;
|
config: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export class App {
|
export class App extends EventEmitter {
|
||||||
private privApp: ElectronApplication | undefined;
|
private privApp: ElectronApplication | undefined;
|
||||||
|
|
||||||
constructor(private readonly options: AppOptionsType) {}
|
constructor(private readonly options: AppOptionsType) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
this.privApp = await electron.launch({
|
this.privApp = await electron.launch({
|
||||||
|
@ -54,6 +57,8 @@ export class App {
|
||||||
},
|
},
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.privApp.on('close', () => this.emit('close'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async waitForProvisionURL(): Promise<string> {
|
public async waitForProvisionURL(): Promise<string> {
|
||||||
|
@ -111,6 +116,29 @@ export class App {
|
||||||
return this.app.firstWindow();
|
return this.app.firstWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EventEmitter types
|
||||||
|
|
||||||
|
public override on(type: 'close', callback: () => void): this;
|
||||||
|
|
||||||
|
public override on(
|
||||||
|
type: string | symbol,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
listener: (...args: Array<any>) => void
|
||||||
|
): this {
|
||||||
|
return super.on(type, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override emit(type: 'close'): boolean;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
public override emit(type: string | symbol, ...args: Array<any>): boolean {
|
||||||
|
return super.emit(type, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Private
|
||||||
|
//
|
||||||
|
|
||||||
private async waitForEvent<T>(event: string): Promise<T> {
|
private async waitForEvent<T>(event: string): Promise<T> {
|
||||||
const window = await this.getWindow();
|
const window = await this.getWindow();
|
||||||
|
|
||||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -14546,17 +14546,17 @@ pkg-dir@^5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
find-up "^5.0.0"
|
find-up "^5.0.0"
|
||||||
|
|
||||||
playwright-core@1.30.0:
|
playwright-core@1.31.2:
|
||||||
version "1.30.0"
|
version "1.31.2"
|
||||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.30.0.tgz#de987cea2e86669e3b85732d230c277771873285"
|
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.31.2.tgz#debf4b215d14cb619adb7e511c164d068075b2ed"
|
||||||
integrity sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g==
|
integrity sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==
|
||||||
|
|
||||||
playwright@1.30.0:
|
playwright@1.31.2:
|
||||||
version "1.30.0"
|
version "1.31.2"
|
||||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.30.0.tgz#b1d7be2d45d97fbb59f829f36f521f12010fe072"
|
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.31.2.tgz#4252280586c596746122cd1fdf9f8ff6a63fa852"
|
||||||
integrity sha512-ENbW5o75HYB3YhnMTKJLTErIBExrSlX2ZZ1C/FzmHjUYIfxj/UnI+DWpQr992m+OQVSg0rCExAOlRwB+x+yyIg==
|
integrity sha512-jpC47n2PKQNtzB7clmBuWh6ftBRS/Bt5EGLigJ9k2QAKcNeYXZkEaDH5gmvb6+AbcE0DO6GnXdbl9ogG6Eh+og==
|
||||||
dependencies:
|
dependencies:
|
||||||
playwright-core "1.30.0"
|
playwright-core "1.31.2"
|
||||||
|
|
||||||
plist@^3.0.1, plist@^3.0.4:
|
plist@^3.0.1, plist@^3.0.4:
|
||||||
version "3.0.5"
|
version "3.0.5"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue