test: add more auto updater tests for squirrel.mac (#24611)

This commit is contained in:
Samuel Attard 2020-07-20 09:51:33 -07:00 committed by GitHub
parent 8f5280a821
commit 682f78b9a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 273 additions and 55 deletions

View file

@ -104,7 +104,7 @@ The `autoUpdater` object has the following methods:
* `options` Object
* `url` String
* `headers` Record<String, String> (optional) _macOS_ - HTTP request headers.
* `serverType` String (optional) _macOS_ - Either `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
* `serverType` String (optional) _macOS_ - Can be `json` or `default`, see the [Squirrel.Mac][squirrel-mac]
README for more information.
Sets the `url` and initialize the auto updater.

View file

@ -73,7 +73,7 @@ ifdescribe(!process.mas)('autoUpdater module', function () {
});
it('does throw if an unknown string is the serverType', () => {
expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'weow' })).to.throw('Expected serverType to be \'default\' or \'json\'');
expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'weow' as any })).to.throw('Expected serverType to be \'default\' or \'json\'');
});
});
});

View file

@ -94,13 +94,15 @@ describeFn('autoUpdater behavior', function () {
return spawn(path.resolve(appPath, 'Contents/MacOS/Electron'), args);
};
const withTempDirectory = async (fn: (dir: string) => Promise<void>) => {
const withTempDirectory = async (fn: (dir: string) => Promise<void>, autoCleanUp = true) => {
const dir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-update-spec-'));
try {
await fn(dir);
} finally {
if (autoCleanUp) {
cp.spawnSync('rm', ['-r', dir]);
}
}
};
const logOnError = (what: any, fn: () => void) => {
@ -112,6 +114,36 @@ describeFn('autoUpdater behavior', function () {
}
};
const cachedZips: Record<string, string> = {};
const getOrCreateUpdateZipPath = async (version: string, fixture: string) => {
const key = `${version}-${fixture}`;
if (!cachedZips[key]) {
let updateZipPath: string;
await withTempDirectory(async (dir) => {
const secondAppPath = await copyApp(dir, fixture);
const appPJPath = path.resolve(secondAppPath, 'Contents', 'Resources', 'app', 'package.json');
await fs.writeFile(
appPJPath,
(await fs.readFile(appPJPath, 'utf8')).replace('1.0.0', version)
);
await signApp(secondAppPath);
updateZipPath = path.resolve(dir, 'update.zip');
await spawn('zip', ['-r', '--symlinks', updateZipPath, './'], {
cwd: dir
});
}, false);
cachedZips[key] = updateZipPath!;
}
return cachedZips[key];
};
after(() => {
for (const version of Object.keys(cachedZips)) {
cp.spawnSync('rm', ['-r', path.dirname(cachedZips[version])]);
}
});
it('should fail to set the feed URL when the app is not signed', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyApp(dir);
@ -150,12 +182,14 @@ describeFn('autoUpdater behavior', function () {
});
});
afterEach((done) => {
afterEach(async () => {
if (httpServer) {
await new Promise(resolve => {
httpServer.close(() => {
httpServer = null as any;
server = null as any;
done();
resolve();
});
});
}
});
@ -177,6 +211,23 @@ describeFn('autoUpdater behavior', function () {
});
});
it('should hit the update endpoint with customer headers when checkForUpdates is called', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyApp(dir, 'check-with-headers');
await signApp(appPath);
server.get('/update-check', (req, res) => {
res.status(204).send();
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult.code).to.equal(0);
expect(requests).to.have.lengthOf(1);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[0].header('x-test')).to.equal('this-is-a-test');
});
});
});
it('should hit the download endpoint when an update is available and error if the file is bad', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyApp(dir, 'update');
@ -205,25 +256,27 @@ describeFn('autoUpdater behavior', function () {
});
});
it('should hit the download endpoint when an update is available and update successfully when the zip is provided', async () => {
const withUpdatableApp = async (opts: {
nextVersion: string;
startFixture: string;
endFixture: string;
}, fn: (appPath: string, zipPath: string) => Promise<void>) => {
await withTempDirectory(async (dir) => {
const appPath = await copyApp(dir, 'update');
const appPath = await copyApp(dir, opts.startFixture);
await signApp(appPath);
// Prepare update
await withTempDirectory(async (dir2) => {
const secondAppPath = await copyApp(dir2, 'update');
const appPJPath = path.resolve(secondAppPath, 'Contents', 'Resources', 'app', 'package.json');
await fs.writeFile(
appPJPath,
(await fs.readFile(appPJPath, 'utf8')).replace('1.0.0', '2.0.0')
);
await signApp(secondAppPath);
const updateZipPath = path.resolve(dir2, 'update.zip');
await spawn('zip', ['-r', '--symlinks', updateZipPath, './'], {
cwd: dir2
});
const updateZipPath = await getOrCreateUpdateZipPath(opts.nextVersion, opts.endFixture);
await fn(appPath, updateZipPath);
});
};
it('should hit the download endpoint when an update is available and update successfully when the zip is provided', async () => {
await withUpdatableApp({
nextVersion: '2.0.0',
startFixture: 'update',
endFixture: 'update'
}, async (appPath, updateZipPath) => {
server.get('/update-file', (req, res) => {
res.download(updateZipPath);
});
@ -258,6 +311,92 @@ describeFn('autoUpdater behavior', function () {
expect(requests[2].header('user-agent')).to.include('Electron/');
});
});
it('should hit the download endpoint when an update is available and update successfully when the zip is provided with JSON update mode', async () => {
await withUpdatableApp({
nextVersion: '2.0.0',
startFixture: 'update-json',
endFixture: 'update-json'
}, async (appPath, updateZipPath) => {
server.get('/update-file', (req, res) => {
res.download(updateZipPath);
});
server.get('/update-check', (req, res) => {
res.json({
currentRelease: '2.0.0',
releases: [
{
version: '2.0.0',
updateTo: {
version: '2.0.0',
url: `http://localhost:${port}/update-file`,
name: 'My Release Name',
notes: 'Theses are some release notes innit',
pub_date: (new Date()).toString()
}
}
]
});
});
const relaunchPromise = new Promise((resolve) => {
server.get('/update-check/updated/:version', (req, res) => {
res.status(204).send();
resolve();
});
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult).to.have.property('code', 0);
expect(launchResult.out).to.include('Update Downloaded');
expect(requests).to.have.lengthOf(2);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[1]).to.have.property('url', '/update-file');
expect(requests[0].header('user-agent')).to.include('Electron/');
expect(requests[1].header('user-agent')).to.include('Electron/');
});
await relaunchPromise;
expect(requests).to.have.lengthOf(3);
expect(requests[2]).to.have.property('url', '/update-check/updated/2.0.0');
expect(requests[2].header('user-agent')).to.include('Electron/');
});
});
it('should hit the download endpoint when an update is available and not update in JSON update mode when the currentRelease is older than the current version', async () => {
await withUpdatableApp({
nextVersion: '0.1.0',
startFixture: 'update-json',
endFixture: 'update-json'
}, async (appPath, updateZipPath) => {
server.get('/update-file', (req, res) => {
res.download(updateZipPath);
});
server.get('/update-check', (req, res) => {
res.json({
currentRelease: '0.1.0',
releases: [
{
version: '0.1.0',
updateTo: {
version: '0.1.0',
url: `http://localhost:${port}/update-file`,
name: 'My Release Name',
notes: 'Theses are some release notes innit',
pub_date: (new Date()).toString()
}
}
]
});
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult).to.have.property('code', 1);
expect(launchResult.out).to.include('No update available');
expect(requests).to.have.lengthOf(1);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[0].header('user-agent')).to.include('Electron/');
});
});
});
});
});

View file

@ -0,0 +1,26 @@
process.on('uncaughtException', (err) => {
console.error(err);
process.exit(1);
});
const { autoUpdater } = require('electron');
autoUpdater.on('error', (err) => {
console.error(err);
process.exit(1);
});
const feedUrl = process.argv[1];
autoUpdater.setFeedURL({
url: feedUrl,
headers: {
'X-test': 'this-is-a-test'
}
});
autoUpdater.checkForUpdates();
autoUpdater.on('update-not-available', () => {
process.exit(0);
});

View file

@ -0,0 +1,5 @@
{
"name": "initial-app",
"version": "1.0.0",
"main": "./index.js"
}

View file

@ -0,0 +1,43 @@
const fs = require('fs');
const path = require('path');
process.on('uncaughtException', (err) => {
console.error(err);
process.exit(1);
});
const { app, autoUpdater } = require('electron');
autoUpdater.on('error', (err) => {
console.error(err);
process.exit(1);
});
const urlPath = path.resolve(__dirname, '../../../../url.txt');
let feedUrl = process.argv[1];
if (!feedUrl || !feedUrl.startsWith('http')) {
feedUrl = `${fs.readFileSync(urlPath, 'utf8')}/${app.getVersion()}`;
} else {
fs.writeFileSync(urlPath, `${feedUrl}/updated`);
}
autoUpdater.setFeedURL({
url: feedUrl,
serverType: 'json'
});
autoUpdater.checkForUpdates();
autoUpdater.on('update-available', () => {
console.log('Update Available');
});
autoUpdater.on('update-downloaded', () => {
console.log('Update Downloaded');
autoUpdater.quitAndInstall();
});
autoUpdater.on('update-not-available', () => {
console.error('No update available');
process.exit(1);
});

View file

@ -0,0 +1,5 @@
{
"name": "initial-app",
"version": "1.0.0",
"main": "./index.js"
}