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
|
||||
* `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.
|
||||
|
|
|
@ -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\'');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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/');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
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…
Reference in a new issue