Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rzhao271 committed Oct 22, 2021
1 parent c2218f0 commit ff49371
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 26 deletions.
106 changes: 84 additions & 22 deletions spec-main/api-app-spec.ts
Expand Up @@ -205,6 +205,12 @@ describe('app module', () => {
});

describe('app.requestSingleInstanceLock', () => {
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');
Expand All @@ -218,52 +224,108 @@ describe('app module', () => {
expect(code1).to.equal(0);
});

async function testArgumentPassing (fixtureName: string, expectedSecondInstanceData: unknown) {
const appPath = path.join(fixturesPath, 'api', fixtureName);
const first = cp.spawn(process.execPath, [appPath]);
const firstExited = emittedOnce(first, 'exit');
async function testArgumentPassing (testArgs: SingleInstanceLockTestArgs) {
const appPath = path.join(fixturesPath, 'api', 'singleton-data');
const first = cp.spawn(process.execPath, [appPath, ...testArgs.args]);

// Wait for the first app to boot.
const firstStdoutLines = first.stdout.pipe(split());
while ((await emittedOnce(firstStdoutLines, 'data')).toString() !== 'started') {
// wait.
}
const data2Promise = emittedOnce(firstStdoutLines, 'data');
const dataPromise = emittedOnce(firstStdoutLines, 'data');

const secondInstanceArgs = [process.execPath, appPath, '--some-switch', 'some-arg'];
const secondInstanceArgs = [process.execPath, appPath, ...testArgs.args, '--some-switch', 'some-arg'];
const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1));
const secondStdoutLines = second.stdout.pipe(split());
const dataAckPromise = emittedOnce(secondStdoutLines, 'data');

const ackData = await dataAckPromise;
const [code2] = await emittedOnce(second, 'exit');
expect(code2).to.equal(1);
const [code1] = await firstExited;
const [code1] = await emittedOnce(first, 'exit');
expect(code1).to.equal(0);
const received = await data2Promise;
const [args, additionalData] = received[0].toString('ascii').split('||');
const dataFromSecondInstance = await dataPromise;
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) {
expect(secondInstanceArgsReceived).to.include(arg,
`argument ${arg} is missing from received second args`);
}
expect(secondInstanceDataReceived).to.be.deep.equal(expectedSecondInstanceData,
`received data ${JSON.stringify(secondInstanceDataReceived)} is not equal to expected data ${JSON.stringify(expectedSecondInstanceData)}.`);
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)}.`);
}

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,
expectedAck: null
});
});

it('passes arguments to the second-instance event', async () => {
const expectedSecondInstanceData = {
level: 1,
testkey: 'testvalue1',
inner: {
level: 2,
testkey: 'testvalue2'
}
};
await testArgumentPassing('singleton-data', expectedSecondInstanceData);
await testArgumentPassing({
args: ['--send-data'],
expectedAdditionalData,
expectedAck: null
});
});

it('gets back an ack after preventing default', async () => {
await testArgumentPassing({
args: ['--send-ack', '--prevent-default'],
expectedAdditionalData: null,
expectedAck
});
});

it('passes arguments to the second-instance event no additional data', async () => {
await testArgumentPassing('singleton', null);
it('is able to send back empty ack after preventing default', async () => {
await testArgumentPassing({
args: ['--prevent-default'],
expectedAdditionalData: null,
expectedAck: null
});
});

it('gets back an empty ack by default', async () => {
await testArgumentPassing({
args: ['--send-ack'],
expectedAdditionalData: null,
expectedAck: null
});
});

it('sends and receives data', async () => {
await testArgumentPassing({
args: ['--send-ack', '--prevent-default', '--send-data'],
expectedAdditionalData,
expectedAck
});
});
});

Expand Down
31 changes: 29 additions & 2 deletions spec/fixtures/api/singleton-data/main.js
Expand Up @@ -4,6 +4,15 @@ app.whenReady().then(() => {
console.log('started'); // ping parent
});

// Send data from the second instance to the first instance.
const sendAdditionalData = app.commandLine.hasSwitch('send-data');

// Prevent the default behaviour of second-instance, which sends back an empty ack.
const preventDefault = app.commandLine.hasSwitch('prevent-default');

// Send an object back for the ack rather than undefined.
const sendAck = app.commandLine.hasSwitch('send-ack');

const obj = {
level: 1,
testkey: 'testvalue1',
Expand All @@ -12,11 +21,29 @@ const obj = {
testkey: 'testvalue2'
}
};
const gotTheLock = app.requestSingleInstanceLock(obj);
const ackObj = {
level: 1,
testkey: 'acktestvalue1',
inner: {
level: 2,
testkey: 'acktestvalue2'
}
};

app.on('first-instance-ack', (event, additionalData) => {
console.log(JSON.stringify(additionalData));
});

const gotTheLock = sendAdditionalData
? app.requestSingleInstanceLock(obj) : app.requestSingleInstanceLock();

app.on('second-instance', (event, args, workingDirectory, data) => {
app.on('second-instance', (event, args, workingDirectory, data, ackCallback) => {
if (preventDefault) {
event.preventDefault();
}
setImmediate(() => {
console.log([JSON.stringify(args), JSON.stringify(data)].join('||'));
sendAck ? ackCallback(ackObj) : ackCallback();
app.exit(0);
});
});
Expand Down
4 changes: 2 additions & 2 deletions spec/fixtures/api/singleton/main.js
Expand Up @@ -6,9 +6,9 @@ app.whenReady().then(() => {

const gotTheLock = app.requestSingleInstanceLock();

app.on('second-instance', (event, args, workingDirectory, data) => {
app.on('second-instance', (event, args, workingDirectory) => {
setImmediate(() => {
console.log([JSON.stringify(args), JSON.stringify(data)].join('||'));
console.log(JSON.stringify(args));
app.exit(0);
});
});
Expand Down

0 comments on commit ff49371

Please sign in to comment.