Files
standardnotes-app-web/packages/desktop/test/packageManager.spec.ts
2022-06-07 11:52:15 -05:00

159 lines
5.0 KiB
TypeScript

import test from 'ava'
import { promises as fs } from 'fs'
import path from 'path'
import proxyquire from 'proxyquire'
import { ensureDirectoryExists, readJSONFile } from '../app/javascripts/Main/Utils/FileUtils'
import { createTmpDir } from './testUtils'
import { AppName } from '../app/javascripts/Main/strings'
import makeFakePaths from './fakePaths'
import { PackageManagerInterface } from '../app/javascripts/Main/Packages/PackageManagerInterface'
const tmpDir = createTmpDir(__filename)
const FakePaths = makeFakePaths(tmpDir.path)
const contentDir = path.join(tmpDir.path, 'Extensions')
let downloadFileCallCount = 0
const { initializePackageManager } = proxyquire('../app/javascripts/Main/Packages/PackageManager', {
'./paths': {
Paths: FakePaths,
'@noCallThru': true,
},
'./networking': {
/** Download a fake component file */
async downloadFile(_src: string, dest: string) {
downloadFileCallCount += 1
if (!path.normalize(dest).startsWith(tmpDir.path)) {
throw new Error(`Bad download destination: ${dest}`)
}
await ensureDirectoryExists(path.dirname(dest))
await fs.copyFile(path.join(__dirname, 'data', 'zip-file.zip'), path.join(dest))
},
},
})
const fakeWebContents = {
send(_eventName: string, { error }) {
if (error) throw error
},
}
const name = 'Fake Component'
const identifier = 'fake.component'
const uuid = 'fake-component'
const version = '1.0.0'
const modifiers = Array(20)
.fill(0)
.map((_, i) => String(i).padStart(2, '0'))
function fakeComponent({ deleted = false, modifier = '' } = {}) {
return {
uuid: uuid + modifier,
deleted,
content: {
name: name + modifier,
autoupdateDisabled: false,
package_info: {
version,
identifier: identifier + modifier,
download_url: 'https://standardnotes.com',
url: 'https://standardnotes.com',
latest_url: 'https://standardnotes.com',
},
},
}
}
let packageManager: PackageManagerInterface
const log = console.log
const error = console.error
test.before(async function () {
/** Silence the package manager's output. */
// eslint-disable-next-line @typescript-eslint/no-empty-function
console.log = () => {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
console.error = () => {}
await ensureDirectoryExists(contentDir)
packageManager = await initializePackageManager(fakeWebContents)
})
test.after.always(async function () {
console.log = log
console.error = error
await tmpDir.clean()
})
test.beforeEach(function () {
downloadFileCallCount = 0
})
test('installs multiple components', async (t) => {
await packageManager.syncComponents(modifiers.map((modifier) => fakeComponent({ modifier })))
await new Promise((resolve) => setTimeout(resolve, 200))
const files = await fs.readdir(contentDir)
t.is(files.length, 1 + modifiers.length)
for (const modifier of modifiers) {
t.true(files.includes(identifier + modifier))
}
t.true(files.includes('mapping.json'))
const mappingContents = await fs.readFile(path.join(contentDir, 'mapping.json'), 'utf8')
t.deepEqual(
JSON.parse(mappingContents),
modifiers.reduce((acc, modifier) => {
acc[uuid + modifier] = {
location: path.join('Extensions', identifier + modifier),
version,
}
return acc
}, {}),
)
const downloads = await fs.readdir(path.join(tmpDir.path, AppName, 'downloads'))
t.is(downloads.length, modifiers.length)
for (const modifier of modifiers) {
t.true(downloads.includes(`${name + modifier}.zip`))
}
for (const modifier of modifiers) {
const componentFiles = await fs.readdir(path.join(contentDir, identifier + modifier))
t.is(componentFiles.length, 2)
}
})
test('uninstalls multiple components', async (t) => {
await packageManager.syncComponents(modifiers.map((modifier) => fakeComponent({ deleted: true, modifier })))
await new Promise((resolve) => setTimeout(resolve, 200))
const files = await fs.readdir(contentDir)
t.deepEqual(files, ['mapping.json'])
t.deepEqual(await readJSONFile(path.join(contentDir, 'mapping.json')), {})
})
test("doesn't download anything when two install/uninstall tasks are queued", async (t) => {
await Promise.all([
packageManager.syncComponents([fakeComponent({ deleted: false })]),
packageManager.syncComponents([fakeComponent({ deleted: false })]),
packageManager.syncComponents([fakeComponent({ deleted: true })]),
])
t.is(downloadFileCallCount, 1)
})
test("Relies on download_url's version field to store the version number", async (t) => {
await packageManager.syncComponents([fakeComponent()])
await new Promise((resolve) => setTimeout(resolve, 200))
const mappingFileVersion = JSON.parse(await fs.readFile(path.join(contentDir, 'mapping.json'), 'utf8'))[uuid].version
const packageJsonVersion = JSON.parse(
await fs.readFile(path.join(contentDir, identifier, 'package.json'), 'utf-8'),
).version
t.not(mappingFileVersion, packageJsonVersion)
t.is(mappingFileVersion, version)
})