From 5df87d380a5662f75b3f9cc73128bcd410f4e541 Mon Sep 17 00:00:00 2001 From: develar Date: Mon, 29 Feb 2016 09:18:32 +0100 Subject: [PATCH] fix: Configure build resources directory #184 Tests refactored to simplify new test addition ESLint added to check test code Closes #184, #196 --- .eslintignore | 2 + .idea/electron-builder.iml | 1 - .idea/runConfigurations/BuildTest.xml | 6 +- cli.js | 2 +- .eslintrc => lib/.eslintrc | 0 lib/osx.js | 3 +- lib/osx.spec.js | 2 +- lib/win.js | 1 - package.json | 48 ++++- src/linuxPackager.ts | 2 +- src/macPackager.ts | 4 +- src/packager.ts | 9 +- src/platformPackager.ts | 18 +- src/winPackager.ts | 13 +- test/.eslintrc.yml | 43 +++++ test/ArtifactPublisherTest.js | 6 +- test/BuildTest.js | 173 ++++-------------- test/CodeSignTest.js | 19 +- test/RepoSlugTest.js | 6 +- .../test-app-no-author-email/package.json | 2 +- test/fixtures/test-app-one/package.json | 2 +- test/fixtures/test-app/package.json | 2 +- test/helpers/avaEx.js | 26 +++ test/helpers/expectedContents.js | 46 +++++ test/helpers/packTester.js | 77 ++++++++ test/helpers/runTests.js | 106 ++++++++++- test/linuxPackagerTest.js | 10 + test/old-cli-test.js | 6 +- test/package.json | 8 - test/winPackagerTest.js | 11 ++ 30 files changed, 443 insertions(+), 211 deletions(-) create mode 100644 .eslintignore rename .eslintrc => lib/.eslintrc (100%) create mode 100644 test/.eslintrc.yml create mode 100644 test/helpers/avaEx.js create mode 100644 test/helpers/expectedContents.js create mode 100644 test/helpers/packTester.js create mode 100644 test/linuxPackagerTest.js delete mode 100644 test/package.json create mode 100644 test/winPackagerTest.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000..6bc991c9bf8 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +test/fixtures/ +test/old-cli-test.js \ No newline at end of file diff --git a/.idea/electron-builder.iml b/.idea/electron-builder.iml index ebf2654843f..b289b290bbd 100644 --- a/.idea/electron-builder.iml +++ b/.idea/electron-builder.iml @@ -10,7 +10,6 @@ - diff --git a/.idea/runConfigurations/BuildTest.xml b/.idea/runConfigurations/BuildTest.xml index a9da6082eba..5911c5394d6 100644 --- a/.idea/runConfigurations/BuildTest.xml +++ b/.idea/runConfigurations/BuildTest.xml @@ -1,5 +1,9 @@ - + + + + + \ No newline at end of file diff --git a/cli.js b/cli.js index 7f1c527dcfa..bbb7301b0e2 100755 --- a/cli.js +++ b/cli.js @@ -79,7 +79,7 @@ builder.build( Object.assign( cli.flags, { * @return {Object} configuration */ function getConfigFromFile( configPath, property ) { - var config = JSON.parse( fs.readFileSync( configPath ) ); + var config = JSON.parse( fs.readFileSync( configPath, 'utf8' ) ); if ( property ) { if ( config[ property ] ) { diff --git a/.eslintrc b/lib/.eslintrc similarity index 100% rename from .eslintrc rename to lib/.eslintrc diff --git a/lib/osx.js b/lib/osx.js index aa7f292b0d5..73ed84e303d 100644 --- a/lib/osx.js +++ b/lib/osx.js @@ -11,6 +11,7 @@ var fs = require( 'fs' ); var path = require( 'path' ); var os = require( 'os' ); var appdmg = require( 'appdmg' ); +var tmp = require( 'tmp' ); /** * Prototype for the osx installer builder @@ -39,7 +40,7 @@ var OSXBuilder = { osx.icon = path.resolve( options.basePath, osx.icon ); osx.contents[ 1 ].path = options.appPath; - var configFilePath = path.join( os.tmpDir(), 'appdmg.json' ); + var configFilePath = tmp.fileSync().name; fs.writeFileSync( configFilePath, JSON.stringify( options.config.osx ) ); diff --git a/lib/osx.spec.js b/lib/osx.spec.js index 5d9e4e9c97c..5433953a884 100644 --- a/lib/osx.spec.js +++ b/lib/osx.spec.js @@ -40,7 +40,7 @@ if ( process.platform === 'darwin' ) { t.equal( options.target, '/somewhere/out/there/Example App.dmg' ); var configFilePath = options.source; - var config = require( configFilePath, 'utf8' ); + var config = JSON.parse( fs.readFileSync( configFilePath, 'utf8' ) ); t.equal( config.background, '/foo/boooom.png' ); t.equal( config.icon, '/foo/icon.icns' ); diff --git a/lib/win.js b/lib/win.js index 562320cfce1..c170b49c368 100644 --- a/lib/win.js +++ b/lib/win.js @@ -93,7 +93,6 @@ var WinBuilder = { options.log( ' $ apt-get install wine nsis -y' ); break; } - options.log( process.env ); return callback( new Error( 'makensis failed' ) ); } ); diff --git a/package.json b/package.json index 4a90bc1958e..b60b471aca1 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "out/index.js", "scripts": { "compile": "tsconfig -i 2 && ts-babel", - "lint": "eslint ./lib cli.js index.js && tslint src/*", + "lint": "eslint ./lib test cli.js index.js && tslint src/*", "pretest": "npm run compile && npm run lint", "test": "node ./test/helpers/runTests.js", "test-nix": "result=\"$(tape index.spec.js ./lib/*.spec.js ./lib/**/*.spec.js)\"; echo \"$result\" | tap-spec; echo \"$result\" | tnyan; ava", @@ -51,12 +51,12 @@ "dependencies": { "bluebird": "^3.3.3", "command-line-args": "^2.1.6", - "electron-packager-tf": "^5.2.2", - "electron-winstaller-temp-fork": "^0.1.0", + "electron-packager-tf": "^5.2.3", + "electron-winstaller-temp-fork": "^2.0.5-beta.0", "fs-extra": "^0.26.5", "gm": "^1.21.1", "hosted-git-info": "^2.1.4", - "lodash.template": "^4.2.1", + "lodash.template": "^4.2.2", "meow": "^3.7.0", "mime": "^1.3.4", "progress": "^1.1.8", @@ -70,10 +70,19 @@ }, "devDependencies": { "ava-tf": "^0.12.3", + "babel-eslint": "^6.0.0-beta.1", "babel-plugin-array-includes": "^2.0.3", - "babel-plugin-transform-es2015-parameters": "^6.6.0", + "babel-plugin-transform-async-to-module-method": "^6.5.2", + "babel-plugin-transform-es2015-modules-commonjs": "^6.6.4", + "babel-plugin-transform-es2015-parameters": "^6.6.4", + "babel-plugin-transform-strict-mode": "^6.6.4", + "babel-register": "^6.6.0", + "electron-download": "^1.4.1", "eslint": "^2.2.0", + "eslint-plugin-ava": "sindresorhus/eslint-plugin-ava", "ghooks": "^1.0.3", + "json-parse-helpfulerror": "^1.0.3", + "option-chain": "^0.1.1", "path-sort": "^0.1.0", "plist": "^1.2.0", "proxyquire": "^1.7.4", @@ -81,22 +90,41 @@ "should": "^8.2.2", "tap-nyan": "0.0.2", "tap-spec": "^4.1.1", - "tape": "^4.4.0", - "ts-babel": "^0.4.3", + "tape": "^4.5.0", + "ts-babel": "^0.4.5", "tsconfig-glob": "^0.4.1", "tsd-generator": "^2.0.2", "tslint": "^3.5.0", - "typescript": "^1.8.2", + "typescript": "^1.8.7", "validate-commit-msg": "^2.1.0" }, "babel": { "plugins": [ "transform-es2015-parameters", "array-includes" - ] + ], + "env": { + "test": { + "ignore": "**/out/*.js", + "plugins": [ + [ + "babel-plugin-transform-async-to-module-method", + { + "module": "bluebird", + "method": "coroutine" + } + ], + "babel-plugin-transform-es2015-modules-commonjs", + "transform-strict-mode" + ] + } + } }, "ava": { - "verbose": true + "verbose": true, + "require": [ + "babel-register" + ] }, "typings": "./out/electron-builder.d.ts", "config": { diff --git a/src/linuxPackager.ts b/src/linuxPackager.ts index cfcd15c76b7..89d1fcc0198 100644 --- a/src/linuxPackager.ts +++ b/src/linuxPackager.ts @@ -33,7 +33,7 @@ export class LinuxPackager extends PlatformPackager { prefix: "png-icons" }) - const outputs = await exec("icns2png", ["-x", "-o", tempDir, path.join(this.projectDir, "build", "icon.icns")]) + const outputs = await exec("icns2png", ["-x", "-o", tempDir, path.join(this.buildResourcesDir, "icon.icns")]) if (!outputs[0].toString().includes("ih32")) { log("48x48 is not found in the icns, 128x128 will be resized") // icns doesn't contain required 48x48, use gm to resize diff --git a/src/macPackager.ts b/src/macPackager.ts index 2231939538d..68b2436cbac 100644 --- a/src/macPackager.ts +++ b/src/macPackager.ts @@ -57,9 +57,9 @@ export default class MacPackager extends PlatformPackager const specification: appdmg.Specification = { title: this.metadata.name, - icon: "build/icon.icns", + icon: path.join(this.buildResourcesDir, "icon.icns"), "icon-size": 80, - background: "build/background.png", + background: path.join(this.buildResourcesDir, "background.png"), contents: [ { "x": 410, "y": 220, "type": "link", "path": "/Applications" diff --git a/src/packager.ts b/src/packager.ts index ec6caafedcd..b0ead44770b 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -69,8 +69,7 @@ export class Packager implements BuildInfo { const distTasks: Array> = [] for (let platform of platforms) { const helper = this.createHelper(platform, cleanupTasks) - const archs = platform === "darwin" ? ["x64"] : (this.options.arch == null || this.options.arch === "all" ? ["ia32", "x64"] : [this.options.arch]) - for (let arch of archs) { + for (let arch of normalizeArchs(platform, this.options.arch)) { await this.installAppDependencies(arch) helper.currentArch = arch @@ -176,7 +175,11 @@ export class Packager implements BuildInfo { } } -function normalizePlatforms(platforms: Array): Array { +export function normalizeArchs(platform: string, arch?: string) { + return platform === "darwin" ? ["x64"] : (arch == null || arch === "all" ? ["ia32", "x64"] : [arch]) +} + +export function normalizePlatforms(platforms: Array): Array { if (platforms == null || platforms.length === 0) { return [process.platform] } diff --git a/src/platformPackager.ts b/src/platformPackager.ts index 5186c71d1a7..59494e5d629 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -12,6 +12,12 @@ const pack = BluebirdPromise.promisify(packager) export interface DevMetadata extends Metadata { build: DevBuildMetadata + + directories?: MetadataDirectories +} + +interface MetadataDirectories { + buildResources?: string } export interface DevBuildMetadata { @@ -57,9 +63,10 @@ export abstract class PlatformPackager implements ProjectMetadataProvider { protected options: PackagerOptions protected projectDir: string + protected buildResourcesDir: string metadata: AppMetadata - devMetadata: Metadata + devMetadata: DevMetadata customDistOptions: DC @@ -73,12 +80,19 @@ export abstract class PlatformPackager implements ProjectMetadataProvider { this.metadata = info.metadata this.devMetadata = info.devMetadata + this.buildResourcesDir = path.resolve(this.projectDir, this.relativeBuildResourcesDirname) + if (this.options.dist) { const buildMetadata: any = info.devMetadata.build this.customDistOptions = buildMetadata == null ? buildMetadata : buildMetadata[this.getBuildConfigurationKey()] } } + protected get relativeBuildResourcesDirname() { + const directories = this.devMetadata.directories + return (directories == null ? null : directories.buildResources) || "build" + } + protected dispatchArtifactCreated(path: string) { this.info.eventEmitter.emit("artifactCreated", path) } @@ -98,7 +112,7 @@ export abstract class PlatformPackager implements ProjectMetadataProvider { platform: platform, arch: this.currentArch, version: this.info.electronVersion, - icon: path.join(this.projectDir, "build", "icon"), + icon: path.join(this.buildResourcesDir, "icon"), asar: true, overwrite: true, "app-version": version, diff --git a/src/winPackager.ts b/src/winPackager.ts index b1116d211ca..7c6d2e54379 100644 --- a/src/winPackager.ts +++ b/src/winPackager.ts @@ -28,7 +28,7 @@ export default class WinPackager extends PlatformPackager { // on appveyor (well, yes, it is a Windows bug) // Because NSIS support will be dropped some day, correct solution is not implemented const iconPath = this.customDistOptions == null ? null : this.customDistOptions.icon - require("../lib/win").copyAssetsToTmpFolder(iconPath || path.join(this.projectDir, "build", "icon.ico")) + require("../lib/win").copyAssetsToTmpFolder(iconPath || path.join(this.buildResourcesDir, "icon.ico")) } // https://developer.mozilla.org/en-US/docs/Signing_an_executable_with_Authenticode @@ -77,12 +77,12 @@ export default class WinPackager extends PlatformPackager { if (this.info.repositoryInfo != null) { const info = await this.info.repositoryInfo.getInfo(this) if (info != null) { - iconUrl = `https://raw.githubusercontent.com/${info.user}/${info.project}/master/build/icon.ico` + iconUrl = `https://raw.githubusercontent.com/${info.user}/${info.project}/master/${this.relativeBuildResourcesDirname}/icon.ico` } } if (!iconUrl) { - throw new Error("iconUrl is not specified, please see https://github.com/develar/electron-complete-builder#in-short") + throw new Error("iconUrl is not specified, please see https://github.com/loopline-systems/electron-builder#in-short") } } } @@ -100,9 +100,9 @@ export default class WinPackager extends PlatformPackager { productName: appName, version: version, description: this.metadata.description, - authors: this.metadata.author, + authors: this.metadata.author.name, iconUrl: iconUrl, - setupIcon: path.join(this.projectDir, "build", "icon.ico"), + setupIcon: path.join(this.buildResourcesDir, "icon.ico"), certificateFile: certificateFile, certificatePassword: this.options.cscKeyPassword, }, this.customDistOptions) @@ -112,8 +112,7 @@ export default class WinPackager extends PlatformPackager { } try { - const build = <(options: any, callback: (error: Error) => void) => void>require("electron-winstaller-temp-fork").build - await BluebirdPromise.promisify(build)(options) + await require("electron-winstaller-temp-fork").createWindowsInstaller(options) } catch (e) { if (!e.message.includes("Unable to set icon")) { diff --git a/test/.eslintrc.yml b/test/.eslintrc.yml new file mode 100644 index 00000000000..b049345ac28 --- /dev/null +++ b/test/.eslintrc.yml @@ -0,0 +1,43 @@ +env: + es6: true + node: true + +parserOptions: + ecmaVersion: 6 + sourceType: module + ecmaFeatures: + impliedStrict: true + +parser: babel-eslint + +plugins: + - ava + +extends: "eslint:recommended" + +rules: + ava/max-asserts: [0, 5] + ava/no-cb-test: 0 + ava/no-identical-title: 2 + ava/no-invalid-end: 2 + ava/no-only-test: 2 + ava/no-skip-assert: 2 + ava/no-skip-test: 2 + ava/prefer-power-assert: 0 + ava/test-ended: 2 + ava/test-title: [2, 'always'] + + "quotes": [ 2, "double" ] + semi: [2, "never"] + "linebreak-style": [ 2, "unix" ] + no-trailing-spaces: 2 + no-var: 2 + comma-dangle: 0 + no-console: 0 + curly: 2 + eqeqeq: [2, "allow-null"] + no-throw-literal: 2 + no-unused-expressions: 2 + no-path-concat: 2 + prefer-arrow-callback: 2 + prefer-const: 2 \ No newline at end of file diff --git a/test/ArtifactPublisherTest.js b/test/ArtifactPublisherTest.js index 2fb20b7a91e..dbe922404a7 100644 --- a/test/ArtifactPublisherTest.js +++ b/test/ArtifactPublisherTest.js @@ -1,13 +1,13 @@ import test from "ava-tf" -import { GitHubPublisher } from "electron-builder/out/gitHubPublisher" +import { GitHubPublisher } from "out/gitHubPublisher" import { join } from "path" function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; + return Math.floor(Math.random() * (max - min + 1)) + min } function versionNumber() { - return getRandomInt(0, 99) + "." + Date.now() + "." + getRandomInt(0, 9); + return getRandomInt(0, 99) + "." + Date.now() + "." + getRandomInt(0, 9) } const token = new Buffer("Y2Y5NDdhZDJhYzJlMzg1OGNiNzQzYzcwOWZhNGI0OTk2NWQ4ZDg3Yg==", "base64").toString() diff --git a/test/BuildTest.js b/test/BuildTest.js index 2527208f171..bb8de6f04d4 100644 --- a/test/BuildTest.js +++ b/test/BuildTest.js @@ -1,152 +1,51 @@ -import test from "ava-tf" +import test from "./helpers/avaEx" +import { assertPack } from "./helpers/packTester" import fse from "fs-extra" -import tmp from "tmp" import Promise from "bluebird" -import assertThat from "should/as-function" import * as path from "path" -import { parse as parsePlist } from "plist" -import { Packager } from "electron-builder" -import { exec } from "electron-builder/out/util" -import { readText } from "electron-builder/out/promisifed-fs" -import { CSC_LINK, CSC_KEY_PASSWORD } from "./helpers/codeSignData" -import pathSorter from "path-sort" +import { readText } from "out/promisifed-fs" +import { parse as parseJson } from "json-parse-helpfulerror" -const copyDir = Promise.promisify(fse.copy) -const tmpDir = Promise.promisify(tmp.dir) - -const expectedLinuxContents = ['/', - '/opt/', - '/usr/', - '/opt/TestApp/', - '/opt/TestApp/content_shell.pak', - '/opt/TestApp/icudtl.dat', - '/opt/TestApp/libnode.so', - '/opt/TestApp/LICENSE', - '/opt/TestApp/LICENSES.chromium.html', - '/opt/TestApp/natives_blob.bin', - '/opt/TestApp/pkgtarget', - '/opt/TestApp/snapshot_blob.bin', - '/opt/TestApp/TestApp', - '/opt/TestApp/version', - '/usr/share/', - '/opt/TestApp/resources/', - '/opt/TestApp/resources/app.asar', - '/opt/TestApp/resources/atom.asar', - '/usr/share/applications/', - '/usr/share/applications/TestApp.desktop', - '/usr/share/doc/', - '/usr/share/icons/', - '/usr/share/doc/testapp/', - '/usr/share/doc/testapp/changelog.Debian.gz', - '/usr/share/icons/hicolor/', - '/usr/share/icons/hicolor/128x128/', - '/usr/share/icons/hicolor/16x16/', - '/usr/share/icons/hicolor/256x256/', - '/usr/share/icons/hicolor/32x32/', - '/usr/share/icons/hicolor/48x48/', - '/usr/share/icons/hicolor/512x512/', - '/usr/share/icons/hicolor/128x128/apps/', - '/usr/share/icons/hicolor/128x128/apps/TestApp.png', - '/usr/share/icons/hicolor/16x16/apps/', - '/usr/share/icons/hicolor/16x16/apps/TestApp.png', - '/usr/share/icons/hicolor/256x256/apps/', - '/usr/share/icons/hicolor/256x256/apps/TestApp.png', - '/usr/share/icons/hicolor/32x32/apps/', - '/usr/share/icons/hicolor/32x32/apps/TestApp.png', - '/usr/share/icons/hicolor/48x48/apps/', - '/usr/share/icons/hicolor/48x48/apps/TestApp.png', - '/usr/share/icons/hicolor/512x512/apps/', - '/usr/share/icons/hicolor/512x512/apps/TestApp.png' -] - -async function assertPack(projectDir, platform, target, useTempDir) { - projectDir = path.join(__dirname, "fixtures", projectDir) - // const isDoNotUseTempDir = platform === "darwin" - if (useTempDir) { - // non-osx test uses the same dir as osx test, but we cannot share node_modules (because tests executed in parallel) - const dir = await tmpDir({ - unsafeCleanup: true, - prefix: platform - }) - await copyDir(projectDir, dir, { - filter: function (p) { - const basename = path.basename(p) - return basename !== "dist" && basename !== "node_modules" && basename[0] !== "." - } - }) - projectDir = dir - } - - const packager = new Packager({ - projectDir: projectDir, - cscLink: CSC_LINK, - cscKeyPassword: CSC_KEY_PASSWORD, - dist: true, - platform: [platform], - target: target - }) - - await packager.build() - if (platform === "darwin") { - const packedAppDir = projectDir + "/dist/TestApp-darwin-x64/TestApp.app" - const info = parsePlist(await readText(packedAppDir + "/Contents/Info.plist")) - assertThat(info).has.properties({ - CFBundleDisplayName: "TestApp", - CFBundleIdentifier: "your.id", - LSApplicationCategoryType: "your.app.category.type", - CFBundleVersion: "1.0.0" + "." + (process.env.TRAVIS_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM) - }) - - const result = await exec("codesign", ["--verify", packedAppDir]) - assertThat(result[0].toString()).not.match(/is not signed at all/) - } - else if (platform === "linux") { - assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb")).deepEqual(expectedLinuxContents) - assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb")).deepEqual(expectedLinuxContents) - // console.log(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb")) - // console.log(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb")) - } -} - -async function getContents(path) { - const result = await exec("dpkg", ["--contents", path]) - return pathSorter(result[0] - .split("\n") - .map(it => it.length === 0 ? null : it.substring(it.indexOf('.') + 1)) - .filter(it => it != null && !(it.startsWith("/opt/TestApp/locales/") || it.startsWith("/opt/TestApp/libgcrypt"))) - ) -} +const writeFile = Promise.promisify(fse.writeFile) +const moveFile = Promise.promisify(fse.move) if (process.env.TRAVIS !== "true") { // we don't use CircleCI, so, we can safely set this env process.env.CIRCLE_BUILD_NUM = 42 } -if (process.platform === "darwin") { - test("mac: two-package.json", async function () { - await assertPack("test-app", "darwin") - }) +test.ifOsx("mac: two-package.json", async function () { + await assertPack("test-app", "darwin") +}) - test("mac: one-package.json", async function () { - await assertPack("test-app-one", "darwin") - }) -} +test.ifOsx("mac: one-package.json", async function () { + await assertPack("test-app-one", "darwin") +}) -if (process.platform !== "win32") { - test("linux", async function () { - await assertPack("test-app-one", "linux") - }) +test("custom app dir", async function () { + let platforms + if (process.platform === "darwin") { + platforms = ["darwin", "linux"] + } + else if (process.platform === "linux") { + // todo install wine on Linux agent + platforms = ["linux"] + } + else { + platforms = ["win32"] + } - test("no-author-email", async (t) => { - t.throws(assertPack("test-app-no-author-email", "linux"), /Please specify author 'email' in .*/) + await assertPack("test-app-one", platforms, [], true, async projectDir => { + const file = path.join(projectDir, "package.json") + const data = parseJson(await readText(file)) + data.directories = { + buildResources: "custom" + } + + return Promise.all([ + writeFile(file, JSON.stringify(data, null, 2)), + moveFile(path.join(projectDir, "build"), path.join(projectDir, "custom")) + ]) }) -} +}) -if (!process.env.TRAVIS) { - test("win", async function () { - await assertPack("test-app-one", "win32") - }) - test("win: nsis", async function () { - await assertPack("test-app-one", "win32", ["nsis"], true) - }) -} \ No newline at end of file diff --git a/test/CodeSignTest.js b/test/CodeSignTest.js index 5e885ae3578..7038f70adba 100644 --- a/test/CodeSignTest.js +++ b/test/CodeSignTest.js @@ -1,23 +1,14 @@ -import { createKeychain, deleteKeychain, generateKeychainName } from "electron-builder/out/codeSign" +import { createKeychain, deleteKeychain, generateKeychainName } from "out/codeSign" import assertThat from "should/as-function" -import avaTest from "ava-tf" +import test from "./helpers/avaEx" import { CSC_NAME, CSC_LINK, CSC_KEY_PASSWORD } from "./helpers/codeSignData" -import promises from "electron-builder/out/promise" +import promises from "out/promise" -function test(doNotSkip, name, testFunction) { - if (doNotSkip) { - avaTest(name, testFunction) - } - else { - avaTest.skip(name, testFunction) - } -} - -test(process.platform === "darwin", "create keychain", async () => { +test.ifOsx("create keychain", async () => { const keychainName = generateKeychainName() await promises.executeFinally(createKeychain(keychainName, CSC_LINK, CSC_KEY_PASSWORD) .then(result => { assertThat(result.cscKeychainName).not.empty() assertThat(result.cscName).equal(CSC_NAME) - }), error => promises.all([deleteKeychain(keychainName)])) + }), () => promises.all([deleteKeychain(keychainName)])) }) \ No newline at end of file diff --git a/test/RepoSlugTest.js b/test/RepoSlugTest.js index 6af28cc491f..95acf057827 100644 --- a/test/RepoSlugTest.js +++ b/test/RepoSlugTest.js @@ -1,8 +1,8 @@ -import { InfoRetriever } from "electron-builder/out/repositoryInfo" +import { InfoRetriever } from "out/repositoryInfo" import assertThat from "should/as-function" import test from "ava-tf" -test("repo slug from TRAVIS_REPO_SLUG", function () { +test("repo slug from TRAVIS_REPO_SLUG", () => { const oldValue = process.env.TRAVIS_REPO_SLUG try { process.env.TRAVIS_REPO_SLUG = "travis-ci/travis-build" @@ -26,7 +26,7 @@ function restoreEnv(name, value) { } } -test("repo slug from APPVEYOR", function () { +test("repo slug from APPVEYOR", () => { const oldAppveyorAccountName = process.env.APPVEYOR_ACCOUNT_NAME const oldAppveyorProjectName = process.env.APPVEYOR_PROJECT_NAME const travisSlug = process.env.TRAVIS_REPO_SLUG diff --git a/test/fixtures/test-app-no-author-email/package.json b/test/fixtures/test-app-no-author-email/package.json index cb88b903213..62d74748411 100644 --- a/test/fixtures/test-app-no-author-email/package.json +++ b/test/fixtures/test-app-no-author-email/package.json @@ -5,7 +5,7 @@ "description": "Test Application", "author": "Foo Bar", "devDependencies": { - "electron-prebuilt": "^0.36.7" + "electron-prebuilt": "^0.36.9" }, "build": { "app-bundle-id": "your.id", diff --git a/test/fixtures/test-app-one/package.json b/test/fixtures/test-app-one/package.json index 6a0bd32e999..355709e9e71 100644 --- a/test/fixtures/test-app-one/package.json +++ b/test/fixtures/test-app-one/package.json @@ -8,7 +8,7 @@ }, "author": "Foo Bar ", "devDependencies": { - "electron-prebuilt": "^0.36.7" + "electron-prebuilt": "^0.36.9" }, "build": { "app-bundle-id": "your.id", diff --git a/test/fixtures/test-app/package.json b/test/fixtures/test-app/package.json index 2c969fe45d2..2079c28dd2e 100644 --- a/test/fixtures/test-app/package.json +++ b/test/fixtures/test-app/package.json @@ -4,6 +4,6 @@ "start": "electron ." }, "devDependencies": { - "electron-prebuilt": "^0.36.7" + "electron-prebuilt": "^0.36.9" } } diff --git a/test/helpers/avaEx.js b/test/helpers/avaEx.js new file mode 100644 index 00000000000..f5a03efb35c --- /dev/null +++ b/test/helpers/avaEx.js @@ -0,0 +1,26 @@ +import test from "ava-tf" + +Object.defineProperties(test, { + "ifNotWindows": { + get: function () { + return process.platform === "win32" ? this.skip : this + } + }, + "ifNotCi": { + get: function () { + return process.env.CI ? this.skip : this + } + }, + "ifNotTravis": { + get: function () { + return process.env.TRAVIS ? this.skip : this + } + }, + "ifOsx": { + get: function () { + return process.platform === "darwin" ? this : this.skip + } + } +}) + +export default test \ No newline at end of file diff --git a/test/helpers/expectedContents.js b/test/helpers/expectedContents.js new file mode 100644 index 00000000000..d5db75e3132 --- /dev/null +++ b/test/helpers/expectedContents.js @@ -0,0 +1,46 @@ +export const expectedLinuxContents = [ + "/", + "/opt/", + "/usr/", + "/opt/TestApp/", + "/opt/TestApp/content_shell.pak", + "/opt/TestApp/icudtl.dat", + "/opt/TestApp/libffmpeg.so", + "/opt/TestApp/libnode.so", + "/opt/TestApp/LICENSE", + "/opt/TestApp/LICENSES.chromium.html", + "/opt/TestApp/natives_blob.bin", + "/opt/TestApp/pkgtarget", + "/opt/TestApp/snapshot_blob.bin", + "/opt/TestApp/TestApp", + "/opt/TestApp/version", + "/usr/share/", + "/opt/TestApp/resources/", + "/opt/TestApp/resources/app.asar", + "/opt/TestApp/resources/atom.asar", + "/usr/share/applications/", + "/usr/share/applications/TestApp.desktop", + "/usr/share/doc/", + "/usr/share/icons/", + "/usr/share/doc/testapp/", + "/usr/share/doc/testapp/changelog.Debian.gz", + "/usr/share/icons/hicolor/", + "/usr/share/icons/hicolor/128x128/", + "/usr/share/icons/hicolor/16x16/", + "/usr/share/icons/hicolor/256x256/", + "/usr/share/icons/hicolor/32x32/", + "/usr/share/icons/hicolor/48x48/", + "/usr/share/icons/hicolor/512x512/", + "/usr/share/icons/hicolor/128x128/apps/", + "/usr/share/icons/hicolor/128x128/apps/TestApp.png", + "/usr/share/icons/hicolor/16x16/apps/", + "/usr/share/icons/hicolor/16x16/apps/TestApp.png", + "/usr/share/icons/hicolor/256x256/apps/", + "/usr/share/icons/hicolor/256x256/apps/TestApp.png", + "/usr/share/icons/hicolor/32x32/apps/", + "/usr/share/icons/hicolor/32x32/apps/TestApp.png", + "/usr/share/icons/hicolor/48x48/apps/", + "/usr/share/icons/hicolor/48x48/apps/TestApp.png", + "/usr/share/icons/hicolor/512x512/apps/", + "/usr/share/icons/hicolor/512x512/apps/TestApp.png" +] \ No newline at end of file diff --git a/test/helpers/packTester.js b/test/helpers/packTester.js new file mode 100644 index 00000000000..8fb875c1050 --- /dev/null +++ b/test/helpers/packTester.js @@ -0,0 +1,77 @@ +import fse from "fs-extra" +import tmp from "tmp" +import Promise from "bluebird" +import assertThat from "should/as-function" +import * as path from "path" +import { parse as parsePlist } from "plist" +import { CSC_LINK, CSC_KEY_PASSWORD } from "./codeSignData" +import { expectedLinuxContents } from "./expectedContents" +import { readText } from "out/promisifed-fs" +import { Packager } from "out/index" +import { exec } from "out/util" +import pathSorter from "path-sort" + +const copyDir = Promise.promisify(fse.copy) +const tmpDir = Promise.promisify(tmp.dir) + +export async function assertPack(projectDir, platform, target, useTempDir, tempDirCreated) { + projectDir = path.join(__dirname, "..", "fixtures", projectDir) + // const isDoNotUseTempDir = platform === "darwin" + if (useTempDir) { + // non-osx test uses the same dir as osx test, but we cannot share node_modules (because tests executed in parallel) + const dir = await tmpDir({ + unsafeCleanup: true, + prefix: platform + }) + await copyDir(projectDir, dir, { + filter: function (p) { + const basename = path.basename(p) + return basename !== "dist" && basename !== "node_modules" && basename[0] !== "." + } + }) + projectDir = dir + + if (tempDirCreated != null) { + await tempDirCreated(projectDir) + } + } + + const packager = new Packager({ + projectDir: projectDir, + cscLink: CSC_LINK, + cscKeyPassword: CSC_KEY_PASSWORD, + dist: true, + platform: Array.isArray(platform) ? platform : [platform], + target: target + }) + + await packager.build() + if (platform === "darwin" || (platform === "all" && process.platform === "darwin")) { + const packedAppDir = projectDir + "/dist/TestApp-darwin-x64/TestApp.app" + const info = parsePlist(await readText(packedAppDir + "/Contents/Info.plist")) + assertThat(info).has.properties({ + CFBundleDisplayName: "TestApp", + CFBundleIdentifier: "your.id", + LSApplicationCategoryType: "your.app.category.type", + CFBundleVersion: "1.0.0" + "." + (process.env.TRAVIS_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM) + }) + + const result = await exec("codesign", ["--verify", packedAppDir]) + assertThat(result[0].toString()).not.match(/is not signed at all/) + } + else if (platform === "linux" || (platform === "all" && process.platform !== "win32")) { + assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb")).deepEqual(expectedLinuxContents) + assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb")).deepEqual(expectedLinuxContents) + // console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb"), null, 2)) + // console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb"), null, 2)) + } +} + +async function getContents(path) { + const result = await exec("dpkg", ["--contents", path]) + return pathSorter(result[0] + .split("\n") + .map(it => it.length === 0 ? null : it.substring(it.indexOf(".") + 1)) + .filter(it => it != null && !(it.startsWith("/opt/TestApp/locales/") || it.startsWith("/opt/TestApp/libgcrypt"))) + ) +} \ No newline at end of file diff --git a/test/helpers/runTests.js b/test/helpers/runTests.js index f90aa683527..0cef97f36da 100644 --- a/test/helpers/runTests.js +++ b/test/helpers/runTests.js @@ -1,23 +1,109 @@ +"use strict" + const childProcess = require("child_process") const path = require("path") -const fs = require("bluebird").promisifyAll(require("fs-extra")) +const Promise = require("bluebird") +const fs = Promise.promisifyAll(require("fs-extra")) +const readText = require("../../out/promisifed-fs").readText +const downloadElectron = Promise.promisify(require("electron-download")) +const packager = require("../../out/packager") -const testPackageDir = path.join(__dirname, "..") +const rootDir = path.join(__dirname, "..", "..") +const testPackageDir = path.join(require("os").tmpdir(), "electron_builder_published") +const testNodeModules = path.join(testPackageDir, "node_modules") -// npm is very slow and not reliable - so, just copy and then prune dev dependencies -const dest = path.join(testPackageDir, "node_modules") -fs.emptyDirAsync(dest) - .then(() => fs.copyAsync(path.join(testPackageDir, "..", "node_modules"), dest)) +const electronVersion = "0.36.9" + +Promise.all([ + deleteOldElectronVersion(), + downloadAllRequiredElectronVersions(), + fs.outputFileAsync(path.join(testPackageDir, "package.json"), `{ + "private": true, + "version": "1.0.0", + "name": "test", + "dependencies": { + "electron-builder": "file:${path.posix.join(__dirname.replace(/\\/g, "/"), "..", "..")}" + } + }`) + .then(() => copyDependencies()) + ]) .then(() => install()) .catch(error => { console.error(error) process.exit(1) }) +function deleteOldElectronVersion() { + if (process.env.CI) { + const cacheDir = path.join(require("os").homedir(), ".electron") + return fs.readdirAsync(cacheDir) + .catch(error => { + if (error.code === "ENOENT") { + return [] + } + else { + throw error + } + }) + .then(it => { + const deletePromises = [] + for (let file of it) { + if (file.endsWith(".zip") && !file.includes(electronVersion)) { + console.log("Remove old electron " + file) + deletePromises.push(fs.unlinkAsync(path.join(cacheDir, file))) + } + } + return Promise.all(deletePromises) + }) + } + else { + return Promise.resolve() + } +} + +function downloadAllRequiredElectronVersions() { + const downloadPromises = [] + for (let platform of packager.normalizePlatforms(["all"])) { + for (let arch of packager.normalizeArchs(platform)) { + downloadPromises.push(downloadElectron({ + version: electronVersion, + arch: arch, + platform: platform, + })) + } + } + return Promise.all(downloadPromises) +} + +function copyDependencies() { +// npm is very slow and not reliable - so, just copy and then prune dev dependencies + return fs.emptyDirAsync(testNodeModules) + .then(() => readText(path.join(rootDir, "package.json"))) + .then(it => { + const devDeps = Object.keys(JSON.parse(it).devDependencies) + const filtered = new Set() + /*eslint prefer-const: 0*/ + for (let name of devDeps) { + filtered.add(path.join(rootDir, "node_modules", name)) + } + + filtered.add(path.join(rootDir, "node_modules", ".bin")) + + return fs.copyAsync(path.join(rootDir, "node_modules"), testNodeModules, { + filter: it => { + if (it.includes("node_modules" + path.sep + "babel-")) { + return false + } + return !filtered.has(it) + } + }) + }) +} + function install() { // install from cache - all dependencies are already installed before run test // https://github.com/npm/npm/issues/2568 - spawn("npm", ["install", "--cache-min", "999999999", "--production", path.join(testPackageDir, "..")], () => { + spawn("npm", ["install", "--cache-min", "999999999", "--production", rootDir], () => { // prune stale packages spawn("npm", ["prune", "--production"], () => { runTests() @@ -30,13 +116,14 @@ function runTests() { }, { cwd: path.join(__dirname, "..", ".."), env: Object.assign({}, process.env, { - NODE_PATH: path.join(testPackageDir, "node_modules") + NODE_PATH: path.join(testNodeModules, "electron-builder"), + BABEL_ENV: "test", }) }) } function spawn(command, args, callback, options) { - if (command == "npm") { + if (command === "npm") { const npmExecPath = process.env.npm_execpath || process.env.NPM_CLI_JS if (npmExecPath != null) { args.unshift(npmExecPath) @@ -62,6 +149,7 @@ function spawn(command, args, callback, options) { console.error(fs.readFileSync(path.join(testPackageDir, "npm-debug.log"), "utf8")) } catch (e) { + // ignore } } diff --git a/test/linuxPackagerTest.js b/test/linuxPackagerTest.js new file mode 100644 index 00000000000..90ff853be73 --- /dev/null +++ b/test/linuxPackagerTest.js @@ -0,0 +1,10 @@ +import test from "./helpers/avaEx" +import { assertPack } from "./helpers/packTester" + +test.ifNotWindows("linux", async function () { + await assertPack("test-app-one", "linux") +}) + +test.ifNotWindows("no-author-email", async(t) => { + t.throws(assertPack("test-app-no-author-email", "linux"), /Please specify author 'email' in .*/) +}) diff --git a/test/old-cli-test.js b/test/old-cli-test.js index 635333cc855..5eca2f649ab 100644 --- a/test/old-cli-test.js +++ b/test/old-cli-test.js @@ -7,7 +7,7 @@ const execFileAsync = Promise.promisify(require('child_process').execFile) const path = require('path') const exampleAppPath = path.join(__dirname, '..', 'example-app') -const cliPath = path.join(__dirname, 'node_modules', '.bin', 'electron-builder') + (process.platform === "win32" ? ".cmd" : "") +const cliPath = path.join(process.env.NODE_PATH, '..', '.bin', 'electron-builder') + (process.platform === "win32" ? ".cmd" : "") function exec(args) { return execFileAsync(cliPath, args, { @@ -39,13 +39,13 @@ if (process.platform === "darwin") { } if (!process.env.CI || process.platform === "win32") { - test('Cli - windows - config file provided', async (t) => { + test.serial('Cli - windows - config file provided', async (t) => { await exec(['Example-win32-ia32', '--platform=win', '--config=builder.json']) t.ok(await fs.statAsync(path.join(exampleAppPath, 'Builder\ Config\ Windows\ example\ Setup.exe')), 'exe created') await fs.removeAsync(path.join(exampleAppPath, 'Builder\ Config\ Windows\ example\ Setup.exe')) }) - test('Cli - windows - no config file provided', async t => { + test.serial('Cli - windows - no config file provided', async t => { await exec(['Example-win32-ia32', '--platform=win']) t.ok(await fs.statAsync(path.join(exampleAppPath, 'Electron\ Builder\ Example\ Setup.exe')), 'exe created') await fs.removeAsync(path.join(exampleAppPath, 'Electron\ Builder\ Example\ Setup.exe')) diff --git a/test/package.json b/test/package.json deleted file mode 100644 index 1d2c6914fad..00000000000 --- a/test/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "private": true, - "version": "1.0.0", - "name": "test", - "dependencies": { - "electron-builder": "file:.." - } -} diff --git a/test/winPackagerTest.js b/test/winPackagerTest.js new file mode 100644 index 00000000000..7766b717489 --- /dev/null +++ b/test/winPackagerTest.js @@ -0,0 +1,11 @@ +import test from "./helpers/avaEx" +import { assertPack } from "./helpers/packTester" + +test.ifNotTravis("win", async function () { + await assertPack("test-app-one", "win32") +}) + +// nsis is deprecated and not thread-safe - just do not run on CI to avoid failures +test.ifNotCi.serial("win: nsis", async function () { + await assertPack("test-app-one", "win32", ["nsis"], true) +}) \ No newline at end of file