feat: Add first-instance-ack event to the app.requestSingleInstanceLock() flow (#31460)

* feat: Add onFirstInstanceAck event for requestSingleInstanceLock

* Add tests

* Apply patch fix

* Add back missing docs

* Rebase

* Listen for exit earlier in test

* Rebase
This commit is contained in:
Raymond Zhao 2022-01-10 08:54:46 -08:00 committed by GitHub
parent 7c16ef1f62
commit 746927c972
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 514 additions and 92 deletions

View file

@ -208,18 +208,23 @@ describe('app module', () => {
interface SingleInstanceLockTestArgs {
args: string[];
expectedAdditionalData: unknown;
expectedAck: unknown;
}
it('prevents the second launch of app', async function () {
this.timeout(120000);
const appPath = path.join(fixturesPath, 'api', 'singleton-data');
this.timeout(60000);
const appPath = path.join(fixturesPath, 'api', 'singleton');
const first = cp.spawn(process.execPath, [appPath]);
const firstExited = emittedOnce(first, 'exit');
await emittedOnce(first.stdout, 'data');
// Start second app when received output.
const second = cp.spawn(process.execPath, [appPath]);
const [code2] = await emittedOnce(second, 'exit');
const secondExited = emittedOnce(second, 'exit');
const [code2] = await secondExited;
expect(code2).to.equal(1);
const [code1] = await emittedOnce(first, 'exit');
const [code1] = await firstExited;
expect(code1).to.equal(0);
});
@ -238,6 +243,11 @@ describe('app module', () => {
const secondInstanceArgs = [process.execPath, appPath, ...testArgs.args, '--some-switch', 'some-arg'];
const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1));
const secondExited = emittedOnce(second, 'exit');
const secondStdoutLines = second.stdout.pipe(split());
let ackData;
while ((ackData = await emittedOnce(secondStdoutLines, 'data'))[0].toString().length === 0) {
// This isn't valid data.
}
const [code2] = await secondExited;
expect(code2).to.equal(1);
@ -247,6 +257,7 @@ describe('app module', () => {
const [args, additionalData] = dataFromSecondInstance[0].toString('ascii').split('||');
const secondInstanceArgsReceived: string[] = JSON.parse(args.toString('ascii'));
const secondInstanceDataReceived = JSON.parse(additionalData.toString('ascii'));
const dataAckReceived = JSON.parse(ackData[0].toString('ascii'));
// Ensure secondInstanceArgs is a subset of secondInstanceArgsReceived
for (const arg of secondInstanceArgs) {
@ -255,69 +266,113 @@ describe('app module', () => {
}
expect(secondInstanceDataReceived).to.be.deep.equal(testArgs.expectedAdditionalData,
`received data ${JSON.stringify(secondInstanceDataReceived)} is not equal to expected data ${JSON.stringify(testArgs.expectedAdditionalData)}.`);
expect(dataAckReceived).to.be.deep.equal(testArgs.expectedAck,
`received data ${JSON.stringify(dataAckReceived)} is not equal to expected data ${JSON.stringify(testArgs.expectedAck)}.`);
}
it('passes arguments to the second-instance event no additional data', async () => {
const expectedAdditionalData = {
level: 1,
testkey: 'testvalue1',
inner: {
level: 2,
testkey: 'testvalue2'
}
};
const expectedAck = {
level: 1,
testkey: 'acktestvalue1',
inner: {
level: 2,
testkey: 'acktestvalue2'
}
};
it('passes arguments to the second-instance event with no additional data', async () => {
await testArgumentPassing({
args: [],
expectedAdditionalData: null
expectedAdditionalData: null,
expectedAck: null
});
});
it('sends and receives JSON object data', async () => {
const expectedAdditionalData = {
level: 1,
testkey: 'testvalue1',
inner: {
level: 2,
testkey: 'testvalue2'
}
};
it('passes arguments to the second-instance event', async () => {
await testArgumentPassing({
args: ['--send-data'],
expectedAdditionalData
expectedAdditionalData,
expectedAck: null
});
});
it('gets back an ack after preventing default', async () => {
await testArgumentPassing({
args: ['--send-ack', '--prevent-default'],
expectedAdditionalData: null,
expectedAck
});
});
it('is able to send back empty ack after preventing default', async () => {
await testArgumentPassing({
args: ['--prevent-default'],
expectedAdditionalData: null,
expectedAck: null
});
});
it('sends and receives data', async () => {
await testArgumentPassing({
args: ['--send-ack', '--prevent-default', '--send-data'],
expectedAdditionalData,
expectedAck
});
});
it('sends and receives numerical data', async () => {
await testArgumentPassing({
args: ['--send-data', '--data-content=2'],
expectedAdditionalData: 2
args: ['--send-ack', '--ack-content=1', '--prevent-default', '--send-data', '--data-content=2'],
expectedAdditionalData: 2,
expectedAck: 1
});
});
it('sends and receives string data', async () => {
await testArgumentPassing({
args: ['--send-data', '--data-content="data"'],
expectedAdditionalData: 'data'
args: ['--send-ack', '--ack-content="ack"', '--prevent-default', '--send-data', '--data-content="data"'],
expectedAdditionalData: 'data',
expectedAck: 'ack'
});
});
it('sends and receives boolean data', async () => {
await testArgumentPassing({
args: ['--send-data', '--data-content=false'],
expectedAdditionalData: false
args: ['--send-ack', '--ack-content=true', '--prevent-default', '--send-data', '--data-content=false'],
expectedAdditionalData: false,
expectedAck: true
});
});
it('sends and receives array data', async () => {
await testArgumentPassing({
args: ['--send-data', '--data-content=[2, 3, 4]'],
expectedAdditionalData: [2, 3, 4]
args: ['--send-ack', '--ack-content=[1, 2, 3]', '--prevent-default', '--send-data', '--data-content=[2, 3, 4]'],
expectedAdditionalData: [2, 3, 4],
expectedAck: [1, 2, 3]
});
});
it('sends and receives mixed array data', async () => {
await testArgumentPassing({
args: ['--send-data', '--data-content=["2", true, 4]'],
expectedAdditionalData: ['2', true, 4]
args: ['--send-ack', '--ack-content=["1", true, 3]', '--prevent-default', '--send-data', '--data-content=["2", false, 4]'],
expectedAdditionalData: ['2', false, 4],
expectedAck: ['1', true, 3]
});
});
it('sends and receives null data', async () => {
await testArgumentPassing({
args: ['--send-data', '--data-content=null'],
expectedAdditionalData: null
args: ['--send-ack', '--ack-content=null', '--prevent-default', '--send-data', '--data-content=null'],
expectedAdditionalData: null,
expectedAck: null
});
});
@ -325,7 +380,8 @@ describe('app module', () => {
try {
await testArgumentPassing({
args: ['--send-ack', '--ack-content="undefined"', '--prevent-default', '--send-data', '--data-content="undefined"'],
expectedAdditionalData: undefined
expectedAdditionalData: undefined,
expectedAck: undefined
});
assert(false);
} catch (e) {