For quite some time, Netlify has been my (free) tool of choice to deploy my blog and other personal projects. It’s very simple to set-up and their documentation is also easy to follow.
But how do you make your Angular tests part of the Netlify build pipeline? Most of the examples suggest: Build command: ng build --prod
What I need: Build command: ng test && ng build --prod
My first issue , ng test
creates an instance of Chrome browser as specified in the karma.conf.js. Pretty simple to solve, run Headless Chrome .
But how do I get Headless Chrome installed on Netlify’s Linux build environment? Puppeteer .
I solved it with a quick npm install puppeteer
.
I’ve updated my package.json:
1 2 3 4 5 6 7 8 9 "scripts" : { "ng" : "ng" , "start" : "ng serve" , "build" : "ng build --prod" , "test" : "ng test" , "test-headless" : "ng test --watch=false --browsers=ChromeHeadless" , "lint" : "ng lint" , "e2e" : "ng e2e" },
I’ve tested everything locally and Build command: npm run-script test-headless && npm run-script build
executed successfully in my Windows environment.
After I’ve pushed the new changes, the build failed - “CHROME_BIN” env variable error.
1 2 3 4 5 6 7 8 9 10 11 12 {"os":"linux","arch":"x64"}) 11:33:41 AM: audited 34037 packages in 15.642s 11:33:41 AM: found 0 vulnerabilities 11:33:41 AM: > beukes-bunch-health-tracker@0.0.0 test /opt/build/repo/ui 11:33:41 AM: > ng test 11:34:02 AM: 10 03 2019 03:34:02.050:INFO [karma-server]: Karma v4.0.1 server started at http://0.0.0.0:9876/ 11:34:02 AM: 10 03 2019 03:34:02.053:INFO [launcher]: Launching browsers ChromeHeadless with concurrency unlimited 11:34:02 AM: 10 03 2019 03:34:02.060:INFO [launcher]: Starting browser Chrome 11:34:02 AM: 10 03 2019 03:34:02.061:ERROR [launcher]: No binary for Chrome browser on your platform. 11:34:02 AM: Please, set "CHROME_BIN" env variable. 11:34:02 AM: npm 11:34:02 AM: ERR! Test failed. See above for more details.
Seems like karma is looking for the path of the chrome executable.
Second issue , how do I set CHROME_BIN env variable path?
Thanks to the folks at Puppeteer they made it easy by just checking the build logs:
1 2 3 4 5 1:00:59 PM: > puppeteer@1.13.0 install /opt/build/repo/ui/node_modules/puppeteer 1:00:59 PM: > node install.js 1:01:07 PM: Chromium downloaded to /opt/build/repo/ui/node_modules/puppeteer/.local-chromium/linux-637110 1:01:07 PM: > node-sass@4.11.0 postinstall /opt/build/repo/ui/node_modules/node-sass 1:01:07 PM: > node scripts/build.js
I’m not fluent in Linux, but I found the printenv
command. It prints all or part of the environment variables.
New plan: Build command: printenv && npm run-script test-headless && npm run-script build
With a bit of trail and error I discovered:CHROME_BIN=/opt/build/repo/ui/node_modules/puppeteer/.local-chromium/linux-637110/chrome-linux/chrome
From the printenv
I’ve learned that env variable PWD=/opt/build/repo/ui
.
I could use the PWD variable like this:CHROME_BIN=${PWD}/node_modules/puppeteer/.local-chromium/linux-637110/chrome-linux/chrome
But, linux-637110
smells like an appended build number.
I used this hack to dynamically look up the linux-xxxxxx
folder name:ls -x -1 ${PWD}/node_modules/puppeteer/.local-chromium
.
My ugly CHROME_BIN env variable (but it works):CHROME_BIN=${PWD}/node_modules/puppeteer/.local-chromium/$(ls -x -1 ${PWD}/node_modules/puppeteer/.local-chromium)/chrome-linux/chrome
One can set a Linux env variable with export
.
My final plan:Build command: export CHROME_BIN=${PWD}/node_modules/puppeteer/.local-chromium/$(ls -x -1 ${PWD}/node_modules/puppeteer/.local-chromium)/chrome-linux/chrome && printenv && npm run-script test-headless && npm run-script build
To save me the time logging into Netlify and update the build command, I’ve added the netlify.toml file.
1 2 3 4 [build] base = "ui" command = "export CHROME_BIN=${PWD}/node_modules/puppeteer/.local-chromium/$(ls -x -1 ${PWD}/node_modules/puppeteer/.local-chromium)/chrome-linux/chrome && printenv && npm run-script test-headless && npm run-script build" publish = "ui/dist/beukes-bunch-health-tracker"
GREEN - build log:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ... ... 7:13:24 PM: LANGUAGE=en_US:en 7:13:24 PM: YARN_VERSION=1.3.2 7:13:24 PM: rvm_ruby_string=ruby-2.3.6 7:13:24 PM: GIMME_GO_VERSION=1.10 7:13:24 PM: CHROME_BIN=/opt/build/repo/ui/node_modules/puppeteer/.local-chromium/linux-637110/chrome-linux/chrome 7:13:24 PM: GOCACHE=/opt/buildhome/.gimme_cache/gocache 7:13:24 PM: GEM_PATH=/opt/buildhome/.rvm/gems/ruby-2.3.6:/opt/buildhome/.rvm/gems/ruby-2.3.6@global 7:13:24 PM: > ng test --watch=false --browsers=ChromeHeadless 7:13:36 PM: 10 03 2019 11:13:36.774:INFO [karma-server]: Karma v4.0.1 server started at http://0.0.0.0:9876/ 7:13:36 PM: 10 03 2019 11:13:36.776:INFO [launcher]: Launching browsers ChromeHeadless with concurrency unlimited 7:13:36 PM: 10 03 2019 11:13:36.780:INFO [launcher]: Starting browser ChromeHeadless 7:13:46 PM: 10 03 2019 11:13:46.808:INFO [HeadlessChrome 74.0.3723 (Linux 0.0.0)]: Connected on socket POrD2-FtM3GJq9JLAAAA with id 8678984 7:13:51 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 0 of 14 SUCCESS (0 secs / 0 secs) 7:13:52 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 1 of 14 SUCCESS (0 secs / 0.85 secs) 7:13:52 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 2 of 14 SUCCESS (0 secs / 1.562 secs) 7:13:53 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 3 of 14 SUCCESS (0 secs / 1.821 secs) 7:13:53 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 4 of 14 SUCCESS (0 secs / 1.85 secs) 7:13:53 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 5 of 14 SUCCESS (0 secs / 2.103 secs) 7:13:53 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 6 of 14 SUCCESS (0 secs / 2.332 secs) 7:13:53 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 7 of 14 SUCCESS (0 secs / 2.363 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 8 of 14 SUCCESS (0 secs / 2.92 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 9 of 14 SUCCESS (0 secs / 2.938 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 10 of 14 SUCCESS (0 secs / 3.219 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 11 of 14 SUCCESS (0 secs / 3.485 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 12 of 14 SUCCESS (0 secs / 3.494 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 13 of 14 SUCCESS (0 secs / 3.501 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 14 of 14 SUCCESS (0 secs / 3.657 secs) 7:13:54 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 14 of 14 SUCCESS (3.682 secs / 3.657 secs) 7:13:54 PM: TOTAL: 14 SUCCESS 7:13:54 PM: TOTAL: 14 SUCCESS 7:13:55 PM: > ng build --prod 7:15:24 PM: Date: 2019-03-10T11:15:24.219Z 7:15:24 PM: Hash: 64ca61b254dcfd5acdc1 7:15:24 PM: Time: 85158ms 7:15:24 PM: chunk {0} runtime.a5dd35324ddfd942bef1.js (runtime) 1.41 kB [entry] [rendered] 7:15:24 PM: chunk {1} es2015-polyfills.4a4cfea0ce682043f4e9.js (es2015-polyfills) 56.4 kB [initial] [rendered] 7:15:24 PM: chunk {2} main.3abc8440f2ae7fd24e18.js (main) 1.59 MB [initial] [rendered] 7:15:24 PM: chunk {3} polyfills.7e5c029b78344f3b7d7c.js (polyfills) 41.1 kB [initial] [rendered] 7:15:24 PM: chunk {4} styles.e230360cfdba5044352a.css (styles) 61.9 kB [initial] [rendered] ... ...
RED - build log:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 ... 7:06:09 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 8 of 14 SUCCESS (0 secs / 2.921 secs) 7:06:09 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 9 of 14 SUCCESS (0 secs / 2.938 secs) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 10 of 14 SUCCESS (0 secs / 3.244 secs) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0) AuthenticationService should be read tokens FAILED 7:06:10 PM: Expected 'Bearer' to be 'BearerRuan'. 7:06:10 PM: at UserContext.<anonymous> (src/app/services/authentication.service.spec.ts:65:31) 7:06:10 PM: at TestBedViewEngine.push../node_modules/@angular/core/fesm5/testing.js.TestBedViewEngine.execute (node_modules/@angular/core/fesm5/testing.js:1822:1) 7:06:10 PM: at UserContext.<anonymous> (node_modules/@angular/core/fesm5/testing.js:1991:29) 7:06:10 PM: at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:391:1) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 11 of 14 (1 FAILED) (0 secs / 3.671 secs) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0) AuthenticationService should be read tokens FAILED 7:06:10 PM: Expected 'Bearer' to be 'BearerRuan'. 7:06:10 PM: at UserContext.<anonymous> (src/app/services/authentication.service.spec.ts:65:31) 7:06:10 PM: at TestBedViewEngine.push../node_modules/@angular/core/fesm5/testing.js.TestBedViewEngine.execute (node_modules/@angular/core/fesm5/testing.js:1822:1) 7:06:10 PM: at UserContext.<anonymous> (node_modules/@angular/core/fesm5/testing.js:1991:29) 7:06:10 PM: at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:391:1) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 12 of 14 (1 FAILED) (0 secs / 3.689 secs) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 13 of 14 (1 FAILED) (0 secs / 3.695 secs) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 14 of 14 (1 FAILED) (0 secs / 3.856 secs) 7:06:10 PM: HeadlessChrome 74.0.3723 (Linux 0.0.0): Executed 14 of 14 (1 FAILED) (3.883 secs / 3.856 secs) 7:06:10 PM: TOTAL: 1 FAILED, 13 SUCCESS 7:06:10 PM: TOTAL: 1 FAILED, 13 SUCCESS 7:06:10 PM: npm 7:06:10 PM: ERR! code ELIFECYCLE 7:06:10 PM: npm 7:06:10 PM: ERR! errno 1 7:06:10 PM: npm ERR! 7:06:10 PM: beukes-bunch-health-tracker@0.0.0 test-headless: `ng test --watch=false --browsers=ChromeHeadless` 7:06:10 PM: npm ERR! 7:06:10 PM: Exit status 1 7:06:10 PM: npm 7:06:10 PM: ERR! 7:06:10 PM: npm 7:06:10 PM: failed during stage 'building site': Build script returned non-zero exit code: 1 7:06:10 PM: ERR! Failed at the beukes-bunch-health-tracker@0.0.0 test-headless script. 7:06:10 PM: npm ERR! This is probably not a problem with npm. There is likely additional logging output above. 7:06:10 PM: npm 7:06:10 PM: ERR! A complete log of this run can be found in: 7:06:10 PM: npm 7:06:10 PM: Shutting down logging, 17 messages pending
Please leave a comment on how to enhance my ball of mud :)
Use it…don’t use it :)
Kudos to: Todd Palmer - Angular Testing with Headless Chrome Alain Chautard - How-to Running Angular tests on continuous integration servers