test: add more auto updater tests for squirrel.mac (#24611)
This commit is contained in:
parent
8f5280a821
commit
682f78b9a8
7 changed files with 273 additions and 55 deletions
|
@ -104,7 +104,7 @@ The `autoUpdater` object has the following methods:
|
||||||
* `options` Object
|
* `options` Object
|
||||||
* `url` String
|
* `url` String
|
||||||
* `headers` Record<String, String> (optional) _macOS_ - HTTP request headers.
|
* `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.
|
README for more information.
|
||||||
|
|
||||||
Sets the `url` and initialize the auto updater.
|
Sets the `url` and initialize the auto updater.
|
||||||
|
|
|
@ -73,7 +73,7 @@ ifdescribe(!process.mas)('autoUpdater module', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does throw if an unknown string is the serverType', () => {
|
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\'');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -94,12 +94,14 @@ describeFn('autoUpdater behavior', function () {
|
||||||
return spawn(path.resolve(appPath, 'Contents/MacOS/Electron'), args);
|
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-'));
|
const dir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-update-spec-'));
|
||||||
try {
|
try {
|
||||||
await fn(dir);
|
await fn(dir);
|
||||||
} finally {
|
} finally {
|
||||||
cp.spawnSync('rm', ['-r', dir]);
|
if (autoCleanUp) {
|
||||||
|
cp.spawnSync('rm', ['-r', dir]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 () => {
|
it('should fail to set the feed URL when the app is not signed', async () => {
|
||||||
await withTempDirectory(async (dir) => {
|
await withTempDirectory(async (dir) => {
|
||||||
const appPath = await copyApp(dir);
|
const appPath = await copyApp(dir);
|
||||||
|
@ -150,12 +182,14 @@ describeFn('autoUpdater behavior', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach((done) => {
|
afterEach(async () => {
|
||||||
if (httpServer) {
|
if (httpServer) {
|
||||||
httpServer.close(() => {
|
await new Promise(resolve => {
|
||||||
httpServer = null as any;
|
httpServer.close(() => {
|
||||||
server = null as any;
|
httpServer = null as any;
|
||||||
done();
|
server = null as any;
|
||||||
|
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 () => {
|
it('should hit the download endpoint when an update is available and error if the file is bad', async () => {
|
||||||
await withTempDirectory(async (dir) => {
|
await withTempDirectory(async (dir) => {
|
||||||
const appPath = await copyApp(dir, 'update');
|
const appPath = await copyApp(dir, 'update');
|
||||||
|
@ -205,57 +256,145 @@ 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) => {
|
await withTempDirectory(async (dir) => {
|
||||||
const appPath = await copyApp(dir, 'update');
|
const appPath = await copyApp(dir, opts.startFixture);
|
||||||
await signApp(appPath);
|
await signApp(appPath);
|
||||||
|
|
||||||
// Prepare update
|
const updateZipPath = await getOrCreateUpdateZipPath(opts.nextVersion, opts.endFixture);
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
server.get('/update-file', (req, res) => {
|
await fn(appPath, updateZipPath);
|
||||||
res.download(updateZipPath);
|
});
|
||||||
});
|
};
|
||||||
server.get('/update-check', (req, res) => {
|
|
||||||
res.json({
|
|
||||||
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;
|
it('should hit the download endpoint when an update is available and update successfully when the zip is provided', async () => {
|
||||||
expect(requests).to.have.lengthOf(3);
|
await withUpdatableApp({
|
||||||
expect(requests[2]).to.have.property('url', '/update-check/updated/2.0.0');
|
nextVersion: '2.0.0',
|
||||||
expect(requests[2].header('user-agent')).to.include('Electron/');
|
startFixture: 'update',
|
||||||
|
endFixture: 'update'
|
||||||
|
}, async (appPath, updateZipPath) => {
|
||||||
|
server.get('/update-file', (req, res) => {
|
||||||
|
res.download(updateZipPath);
|
||||||
|
});
|
||||||
|
server.get('/update-check', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
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 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/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
26
spec-main/fixtures/auto-update/check-with-headers/index.js
Normal file
26
spec-main/fixtures/auto-update/check-with-headers/index.js
Normal 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);
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "initial-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "./index.js"
|
||||||
|
}
|
43
spec-main/fixtures/auto-update/update-json/index.js
Normal file
43
spec-main/fixtures/auto-update/update-json/index.js
Normal 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);
|
||||||
|
});
|
5
spec-main/fixtures/auto-update/update-json/package.json
Normal file
5
spec-main/fixtures/auto-update/update-json/package.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "initial-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "./index.js"
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue