Browse Source

chore: make the 'npm run test' command work out of the box (#14602)

Samuel Attard 6 years ago
parent
commit
05783d56f7
6 changed files with 100 additions and 13 deletions
  1. 2 6
      .circleci/config.yml
  2. 3 0
      .gitignore
  3. 9 0
      DEPS
  4. 2 6
      appveyor.yml
  5. 2 1
      package.json
  6. 82 0
      script/spec-runner.js

+ 2 - 6
.circleci/config.yml

@@ -119,9 +119,7 @@ build-steps: &build-steps
           if [ "$RUN_TESTS" != "false" ]; then
             cd src
             ninja -C out/Default third_party/electron_node:headers
-            export npm_config_nodedir="$PWD/out/Default/gen/node_headers"
-            (cd electron/spec && npm install)
-            python electron/script/lib/dbus_mock.py ./out/Default/electron electron/spec --ci --enable-logging
+            (cd electron && npm run test -- --ci --enable-logging)
           fi
     - <<: *notify-slack-failure
     - <<: *notify-slack-success
@@ -197,9 +195,7 @@ mac-build-steps: &mac-build-steps
           if [ "$RUN_TESTS" != "false" ]; then
             cd src
             ninja -C out/Default third_party/electron_node:headers
-            export npm_config_nodedir="$PWD/out/Default/gen/node_headers"
-            (cd electron/spec && npm install)
-            ./out/Default/Electron.app/Contents/MacOS/Electron electron/spec --ci --enable-logging
+            (cd electron && npm run test -- --ci --enable-logging)
           fi
     - <<: *notify-slack-failure
     - <<: *notify-slack-success

+ 3 - 0
.gitignore

@@ -56,3 +56,6 @@ compile_commands.json
 # Generated API definitions
 electron-api.json
 electron.d.ts
+
+# Spec hash calculation
+spec/.hash

+ 9 - 0
DEPS

@@ -61,6 +61,15 @@ hooks = [
     'pattern': 'src/electron/package.json',
     'name': 'electron_npm_deps'
   },
+  {
+    'action': [
+      'python',
+      '-c',
+      'import os; os.chdir("src"); os.chdir("electron/spec"); os.system("npm install")',
+    ],
+    'pattern': 'src/electron/spec/package.json',
+    'name': 'electron_spec_npm_deps'
+  },
 ]
 
 recursedeps = [

+ 2 - 6
appveyor.yml

@@ -34,14 +34,10 @@ test_script:
   - ps: >-
       if ($env:GN_CONFIG -eq 'testing') {
         ninja -C out/Default third_party/electron_node:headers
-        $env:npm_config_nodedir="$pwd/out/Default/gen/node_headers"
-        $env:npm_config_msvs_version="2017"
         New-Item .\out\Default\gen\node_headers\Release -Type directory
         Copy-Item -path .\out\Default\electron.lib -destination .\out\Default\gen\node_headers\Release\node.lib
-        Push-Location; cd electron/spec
-        npm install
-        Pop-Location
       } else {
         echo "Skipping tests for $env:GN_CONFIG build"
       }
-  - if "%GN_CONFIG%"=="testing" ( echo Running test suite & .\out\Default\electron.exe electron\spec --ci )
+  - cd electron
+  - if "%GN_CONFIG%"=="testing" ( echo Running test suite & npm run test -- --ci )

+ 2 - 1
package.json

@@ -14,6 +14,7 @@
     "electabul": "~0.0.4",
     "electron-docs-linter": "^2.3.4",
     "electron-typescript-definitions": "^2.0.0",
+    "folder-hash": "^2.1.1",
     "github": "^9.2.0",
     "html-entities": "^1.2.1",
     "husky": "^0.14.3",
@@ -72,7 +73,7 @@
     "release": "node ./script/release.js",
     "repl": "python ./script/start.py --interactive",
     "start": "python ./script/start.py",
-    "test": "python ./script/test.py"
+    "test": "node ./script/spec-runner.js electron/spec"
   },
   "license": "MIT",
   "author": "Electron Community",

+ 82 - 0
script/spec-runner.js

@@ -0,0 +1,82 @@
+const cp = require('child_process')
+const crypto = require('crypto')
+const fs = require('fs')
+const { hashElement } = require('folder-hash')
+const path = require('path')
+
+const BASE = path.resolve(__dirname, '../..')
+const NPM_CMD = process.platform === 'win32' ? 'npm.cmd' : 'npm'
+const OUT_DIR = process.env.ELECTRON_SPEC_OUT_DIR || 'Default'
+
+const specHashPath = path.resolve(__dirname, '../spec/.hash')
+
+const [lastSpecHash, lastSpecInstallHash] = fs.existsSync(specHashPath)
+  ? fs.readFileSync(specHashPath, 'utf8').split('\n')
+  : ['invalid', 'invalid']
+
+getSpecHash().then(([currentSpecHash, currentSpecInstallHash]) => {
+  const specChanged = currentSpecHash !== lastSpecHash
+  const installChanged = lastSpecInstallHash !== currentSpecInstallHash
+  if (specChanged || installChanged) {
+    const out = cp.spawnSync(NPM_CMD, ['install'], {
+      env: Object.assign({}, process.env, {
+        npm_config_nodedir: path.resolve(BASE, `out/${OUT_DIR}/gen/node_headers`),
+        npm_config_msvs_version: '2017'
+      }),
+      cwd: path.resolve(__dirname, '../spec')
+    })
+    if (out.status !== 0) {
+      console.error('Failed to npm install in the spec folder')
+      process.exit(1)
+    }
+    return getSpecHash()
+      .then(([newSpecHash, newSpecInstallHash]) => {
+        fs.writeFileSync(specHashPath, `${newSpecHash}\n${newSpecInstallHash}`)
+      })
+  }
+}).then(() => {
+  let exe = path.resolve(BASE, getElectronExec())
+  const args = process.argv.slice(2)
+  if (process.platform === 'linux') {
+    args.unshift(path.resolve(__dirname, 'lib/dbus_mock.py'), exe)
+    exe = 'python'
+  }
+  const child = cp.spawn(exe, args, {
+    cwd: path.resolve(__dirname, '../..'),
+    stdio: 'inherit'
+  })
+  child.on('exit', (code) => {
+    process.exit(code)
+  })
+})
+
+function getElectronExec () {
+  switch (process.platform) {
+    case 'darwin':
+      return 'out/Default/Electron.app/Contents/MacOS/Electron'
+    case 'win32':
+      return 'out/Default/electron.exe'
+    case 'linux':
+      return 'out/Default/electron'
+    default:
+      throw new Error('Unknown path')
+  }
+}
+
+function getSpecHash () {
+  return Promise.all([
+    new Promise((resolve) => {
+      const hasher = crypto.createHash('SHA256')
+      hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json')))
+      hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package-lock.json')))
+      resolve(hasher.digest('hex'))
+    }),
+    new Promise((resolve, reject) => {
+      const specNodeModulesPath = path.resolve(__dirname, '../spec/node_modules')
+      if (!fs.existsSync(specNodeModulesPath)) {
+        return resolve('invalid')
+      }
+      hashElement(specNodeModulesPath).then((result) => resolve(result.hash)).catch(reject)
+    })
+  ])
+}