mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2024-12-22 10:08:55 +00:00
Add playwright integration testing to the app (PR #1397)
This PR allows us to code playwright integration tests that can potentially replace some of our manual tests and allow us automated testing of the app itself. Current technical limitations: * No app level keyboard simulation support (e.g, zoom in, zoom out, etc.) * No code coverage support, so even though we are testing the app, the code coverage does not reflect this fact
This commit is contained in:
parent
c42c63a8b0
commit
e664bc6af8
53
.github/manual-test
vendored
53
.github/manual-test
vendored
@ -60,10 +60,6 @@ node ./lib/cli.js 'https://npmjs.com/' \
|
|||||||
"$tmp_dir"
|
"$tmp_dir"
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 1: Test checklist *****
|
printf '\n***** SMOKE TEST 1: Test checklist *****
|
||||||
- Injected js: should show an alert saying hello
|
|
||||||
- Injected css: should make npmjs all blue
|
|
||||||
- Internal links open internally
|
|
||||||
- External links open in browser
|
|
||||||
- Context menu -> Open Link In New Window works
|
- Context menu -> Open Link In New Window works
|
||||||
- MAC ONLY: Context menu -> Open Link In New Tab works
|
- MAC ONLY: Context menu -> Open Link In New Tab works
|
||||||
- Keyboard shortcuts: {back, forward, zoom in/out/zero} work
|
- Keyboard shortcuts: {back, forward, zoom in/out/zero} work
|
||||||
@ -73,52 +69,15 @@ request_feedback "$tmp_dir"
|
|||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
printf "\n***** SMOKE TEST 2: Setting up test and building app... *****\n"
|
printf '\n***** SMOKE TEST 2: Setting up test and building app... *****\n'
|
||||||
tmp_dir=$(mktemp -d -t nativefier-manual-test-auth-XXXXX)
|
|
||||||
name="nativefier-smoke-test-2"
|
|
||||||
# Removing for now as httpbin is not presently up
|
|
||||||
# node ./lib/cli.js 'http://httpbin.org/basic-auth/foo/bar' \
|
|
||||||
node ./lib/cli.js 'https://authenticationtest.com/HTTPAuth/' \
|
|
||||||
--basic-auth-username user \
|
|
||||||
--basic-auth-password pass \
|
|
||||||
--name "$name" \
|
|
||||||
"$tmp_dir"
|
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 2: Test checklist *****
|
|
||||||
- Was successfully logged in via HTTP Basic Auth. Should see a "Login Success" and a green banner.
|
|
||||||
- Console: no Electron runtime deprecation warnings/error logged'
|
|
||||||
launch_app "$tmp_dir" "$name"
|
|
||||||
request_feedback "$tmp_dir"
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 3: Setting up test and building app... *****\n'
|
|
||||||
tmp_dir=$(mktemp -d -t nativefier-manual-test-auth-prompt-XXXXX)
|
|
||||||
name='nativefier-smoke-test-3'
|
|
||||||
# node ./lib/cli.js 'http://httpbin.org/basic-auth/foo/bar' \
|
|
||||||
node ./lib/cli.js 'https://authenticationtest.com/HTTPAuth/' \
|
|
||||||
--name "$name" \
|
|
||||||
"$tmp_dir"
|
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 3: Test checklist *****
|
|
||||||
- Should get a login window. Log in with username="user" and password="pass".
|
|
||||||
- Post login, you should see a "Login Success" and a green banner.
|
|
||||||
- Console: no Electron runtime deprecation warnings/error logged'
|
|
||||||
|
|
||||||
launch_app "$tmp_dir" "$name"
|
|
||||||
request_feedback "$tmp_dir"
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 4: Setting up test and building app... *****\n'
|
|
||||||
tmp_dir=$(mktemp -d -t nativefier-manual-test-tray-XXXXX)
|
tmp_dir=$(mktemp -d -t nativefier-manual-test-tray-XXXXX)
|
||||||
name='nativefier-smoke-test-4'
|
name='nativefier-smoke-test-2'
|
||||||
node ./lib/cli.js 'https://google.com/' \
|
node ./lib/cli.js 'https://google.com/' \
|
||||||
--name "$name" \
|
--name "$name" \
|
||||||
--tray \
|
--tray \
|
||||||
"$tmp_dir"
|
"$tmp_dir"
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 4: Test checklist *****
|
printf '\n***** SMOKE TEST 2: Test checklist *****
|
||||||
- Should have an app with a tray icon
|
- Should have an app with a tray icon
|
||||||
- Console: no Electron runtime deprecation warnings/error logged'
|
- Console: no Electron runtime deprecation warnings/error logged'
|
||||||
|
|
||||||
@ -127,15 +86,15 @@ request_feedback "$tmp_dir"
|
|||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 5: Setting up test and building app... *****\n'
|
printf '\n***** SMOKE TEST 3: Setting up test and building app... *****\n'
|
||||||
tmp_dir=$(mktemp -d -t nativefier-manual-test-start-in-tray-XXXXX)
|
tmp_dir=$(mktemp -d -t nativefier-manual-test-start-in-tray-XXXXX)
|
||||||
name='nativefier-smoke-test-5'
|
name='nativefier-smoke-test-3'
|
||||||
node ./lib/cli.js 'https://google.com/' \
|
node ./lib/cli.js 'https://google.com/' \
|
||||||
--name "$name" \
|
--name "$name" \
|
||||||
--tray start-in-tray \
|
--tray start-in-tray \
|
||||||
"$tmp_dir"
|
"$tmp_dir"
|
||||||
|
|
||||||
printf '\n***** SMOKE TEST 5: Test checklist *****
|
printf '\n***** SMOKE TEST 3: Test checklist *****
|
||||||
- Should have an app that does not show a window initially,
|
- Should have an app that does not show a window initially,
|
||||||
but will have a tray icon that will show the window.
|
but will have a tray icon that will show the window.
|
||||||
- Console: no Electron runtime deprecation warnings/error logged'
|
- Console: no Electron runtime deprecation warnings/error logged'
|
||||||
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -39,8 +39,12 @@ jobs:
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
app/package-lock.json
|
app/package-lock.json
|
||||||
# Will also (through `prepare` hook): 1. install ./app, and 2. build
|
# Will also (through `prepare` hook): 1. install ./app, and 2. build
|
||||||
- run: npm ci --no-fund
|
- env:
|
||||||
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||||
|
run: npm ci --no-fund
|
||||||
# Only run linter once, for faster CI. Align the versions of Node here with above and publish.yml.
|
# Only run linter once, for faster CI. Align the versions of Node here with above and publish.yml.
|
||||||
- if: matrix.platform == 'ubuntu-latest' && matrix.node-version == '17'
|
- if: matrix.platform == 'ubuntu-latest' && matrix.node-version == '17'
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
- run: npm test
|
- run: npm run test -- --testPathIgnorePatterns ".*playwright.*"
|
||||||
|
- if: matrix.platform != 'ubuntu-latest' # Doesn't work on non-GUI ubuntu
|
||||||
|
run: npm run test:playwright
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,6 +8,8 @@ app/dist/*
|
|||||||
built-tests
|
built-tests
|
||||||
|
|
||||||
# commit a placeholder to keep the app/lib directory
|
# commit a placeholder to keep the app/lib directory
|
||||||
|
app/inject
|
||||||
|
!app/inject/_placeholder
|
||||||
!app/lib/.placeholder
|
!app/lib/.placeholder
|
||||||
|
|
||||||
dist
|
dist
|
||||||
|
206
app/npm-shrinkwrap.json
generated
206
app/npm-shrinkwrap.json
generated
@ -64,9 +64,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "16.11.26",
|
"version": "16.11.27",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz",
|
||||||
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==",
|
"integrity": "sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
@ -270,16 +270,20 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/define-properties": {
|
"node_modules/define-properties": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
||||||
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
|
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"object-keys": "^1.0.12"
|
"has-property-descriptors": "^1.0.0",
|
||||||
|
"object-keys": "^1.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/detect-node": {
|
"node_modules/detect-node": {
|
||||||
@ -296,9 +300,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "18.0.3",
|
"version": "18.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-18.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-18.0.4.tgz",
|
||||||
"integrity": "sha512-QRUZkGL8O/8CyDmTLSjBeRsZmGTPlPVeWnnpkdNqgHYYaOc/A881FKMiNzvQ9Cj0a+rUavDdwBUfUL82U3Ay7w==",
|
"integrity": "sha512-xfsozNpFr3WzeM1EFlw2qqiqXbCrgQNBJJMlcC4/DUYVpkF8364SZenX7FFFA42NmwXiOEahkvvho/u7UrAcGg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -517,6 +521,28 @@
|
|||||||
"node": ">=6 <7 || >=8"
|
"node": ">=6 <7 || >=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/get-intrinsic": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"has": "^1.0.3",
|
||||||
|
"has-symbols": "^1.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-stream": {
|
"node_modules/get-stream": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||||
@ -548,19 +574,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/global-agent/node_modules/semver": {
|
"node_modules/global-agent/node_modules/semver": {
|
||||||
"version": "7.3.6",
|
"version": "7.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||||
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
|
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^7.4.0"
|
"lru-cache": "^6.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/global-tunnel-ng": {
|
"node_modules/global-tunnel-ng": {
|
||||||
@ -623,6 +649,45 @@
|
|||||||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/has": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-property-descriptors": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-symbols": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/http-cache-semantics": {
|
"node_modules/http-cache-semantics": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||||
@ -723,13 +788,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "7.8.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/matcher": {
|
"node_modules/matcher": {
|
||||||
@ -1186,6 +1254,13 @@
|
|||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/yauzl": {
|
"node_modules/yauzl": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||||
@ -1231,9 +1306,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "16.11.26",
|
"version": "16.11.27",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz",
|
||||||
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==",
|
"integrity": "sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
@ -1389,13 +1464,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"define-properties": {
|
"define-properties": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
|
||||||
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
|
"integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"object-keys": "^1.0.12"
|
"has-property-descriptors": "^1.0.0",
|
||||||
|
"object-keys": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"detect-node": {
|
"detect-node": {
|
||||||
@ -1412,9 +1488,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "18.0.3",
|
"version": "18.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-18.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-18.0.4.tgz",
|
||||||
"integrity": "sha512-QRUZkGL8O/8CyDmTLSjBeRsZmGTPlPVeWnnpkdNqgHYYaOc/A881FKMiNzvQ9Cj0a+rUavDdwBUfUL82U3Ay7w==",
|
"integrity": "sha512-xfsozNpFr3WzeM1EFlw2qqiqXbCrgQNBJJMlcC4/DUYVpkF8364SZenX7FFFA42NmwXiOEahkvvho/u7UrAcGg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.13.0",
|
"@electron/get": "^1.13.0",
|
||||||
@ -1591,6 +1667,25 @@
|
|||||||
"universalify": "^0.1.0"
|
"universalify": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"function-bind": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"get-intrinsic": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"has": "^1.0.3",
|
||||||
|
"has-symbols": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"get-stream": {
|
"get-stream": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||||
@ -1616,13 +1711,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "7.3.6",
|
"version": "7.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||||
"integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==",
|
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lru-cache": "^7.4.0"
|
"lru-cache": "^6.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1675,6 +1770,33 @@
|
|||||||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"has": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"function-bind": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-property-descriptors": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"get-intrinsic": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-symbols": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"http-cache-semantics": {
|
"http-cache-semantics": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||||
@ -1759,11 +1881,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lru-cache": {
|
"lru-cache": {
|
||||||
"version": "7.8.1",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
"integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==",
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"matcher": {
|
"matcher": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@ -2123,6 +2248,13 @@
|
|||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"yauzl": {
|
"yauzl": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||||
|
@ -4,8 +4,9 @@ import {
|
|||||||
NewWindowWebContentsEvent,
|
NewWindowWebContentsEvent,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
import contextMenu from 'electron-context-menu';
|
import contextMenu from 'electron-context-menu';
|
||||||
import log from 'loglevel';
|
|
||||||
import { nativeTabsSupported, openExternal } from '../helpers/helpers';
|
import { nativeTabsSupported, openExternal } from '../helpers/helpers';
|
||||||
|
import * as log from '../helpers/loggingHelper';
|
||||||
import { setupNativefierWindow } from '../helpers/windowEvents';
|
import { setupNativefierWindow } from '../helpers/windowEvents';
|
||||||
import { createNewWindow } from '../helpers/windowHelpers';
|
import { createNewWindow } from '../helpers/windowHelpers';
|
||||||
import {
|
import {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import * as log from 'loglevel';
|
|
||||||
|
|
||||||
import { BrowserWindow, ipcMain } from 'electron';
|
import { BrowserWindow, ipcMain } from 'electron';
|
||||||
|
|
||||||
|
import * as log from '../helpers/loggingHelper';
|
||||||
|
|
||||||
export async function createLoginWindow(
|
export async function createLoginWindow(
|
||||||
loginCallback: (username?: string, password?: string) => void,
|
loginCallback: (username?: string, password?: string) => void,
|
||||||
parent?: BrowserWindow,
|
parent?: BrowserWindow,
|
||||||
|
@ -3,14 +3,17 @@ import * as path from 'path';
|
|||||||
|
|
||||||
import { ipcMain, BrowserWindow, Event } from 'electron';
|
import { ipcMain, BrowserWindow, Event } from 'electron';
|
||||||
import windowStateKeeper from 'electron-window-state';
|
import windowStateKeeper from 'electron-window-state';
|
||||||
import log from 'loglevel';
|
|
||||||
|
|
||||||
|
import { initContextMenu } from './contextMenu';
|
||||||
|
import { createMenu } from './menu';
|
||||||
import {
|
import {
|
||||||
getAppIcon,
|
getAppIcon,
|
||||||
getCounterValue,
|
getCounterValue,
|
||||||
isOSX,
|
isOSX,
|
||||||
nativeTabsSupported,
|
nativeTabsSupported,
|
||||||
} from '../helpers/helpers';
|
} from '../helpers/helpers';
|
||||||
|
import * as log from '../helpers/loggingHelper';
|
||||||
|
import { IS_PLAYWRIGHT } from '../helpers/playwrightHelpers';
|
||||||
import { onNewWindow, setupNativefierWindow } from '../helpers/windowEvents';
|
import { onNewWindow, setupNativefierWindow } from '../helpers/windowEvents';
|
||||||
import {
|
import {
|
||||||
clearCache,
|
clearCache,
|
||||||
@ -18,8 +21,6 @@ import {
|
|||||||
getDefaultWindowOptions,
|
getDefaultWindowOptions,
|
||||||
hideWindow,
|
hideWindow,
|
||||||
} from '../helpers/windowHelpers';
|
} from '../helpers/windowHelpers';
|
||||||
import { initContextMenu } from './contextMenu';
|
|
||||||
import { createMenu } from './menu';
|
|
||||||
import {
|
import {
|
||||||
OutputOptions,
|
OutputOptions,
|
||||||
outputOptionsToWindowOptions,
|
outputOptionsToWindowOptions,
|
||||||
@ -77,6 +78,11 @@ export async function createMainWindow(
|
|||||||
...getDefaultWindowOptions(outputOptionsToWindowOptions(options)),
|
...getDefaultWindowOptions(outputOptionsToWindowOptions(options)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Just load about:blank to start, gives playwright something to latch onto initially for testing.
|
||||||
|
if (IS_PLAYWRIGHT) {
|
||||||
|
await mainWindow.loadURL('about:blank');
|
||||||
|
}
|
||||||
|
|
||||||
mainWindowState.manage(mainWindow);
|
mainWindowState.manage(mainWindow);
|
||||||
|
|
||||||
// after first run, no longer force maximize to be true
|
// after first run, no longer force maximize to be true
|
||||||
|
@ -8,9 +8,9 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
MenuItemConstructorOptions,
|
MenuItemConstructorOptions,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
import * as log from 'loglevel';
|
|
||||||
|
|
||||||
import { cleanupPlainText, isOSX, openExternal } from '../helpers/helpers';
|
import { cleanupPlainText, isOSX, openExternal } from '../helpers/helpers';
|
||||||
|
import * as log from '../helpers/loggingHelper';
|
||||||
import {
|
import {
|
||||||
clearAppData,
|
clearAppData,
|
||||||
getCurrentURL,
|
getCurrentURL,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { app, Tray, Menu, ipcMain, nativeImage, BrowserWindow } from 'electron';
|
import { app, Tray, Menu, ipcMain, nativeImage, BrowserWindow } from 'electron';
|
||||||
import log from 'loglevel';
|
|
||||||
|
|
||||||
import { getAppIcon, getCounterValue, isOSX } from '../helpers/helpers';
|
import { getAppIcon, getCounterValue, isOSX } from '../helpers/helpers';
|
||||||
|
import * as log from '../helpers/loggingHelper';
|
||||||
import { OutputOptions } from '../../../shared/src/options/model';
|
import { OutputOptions } from '../../../shared/src/options/model';
|
||||||
|
|
||||||
export function createTrayIcon(
|
export function createTrayIcon(
|
||||||
|
@ -3,7 +3,8 @@ import * as os from 'os';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { BrowserWindow, OpenExternalOptions, shell } from 'electron';
|
import { BrowserWindow, OpenExternalOptions, shell } from 'electron';
|
||||||
import * as log from 'loglevel';
|
|
||||||
|
import * as log from '../helpers/loggingHelper';
|
||||||
|
|
||||||
export const INJECT_DIR = path.join(__dirname, '..', 'inject');
|
export const INJECT_DIR = path.join(__dirname, '..', 'inject');
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import log from 'loglevel';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { isOSX, isWindows, isLinux } from './helpers';
|
import { isOSX, isWindows, isLinux } from './helpers';
|
||||||
|
import * as log from './loggingHelper';
|
||||||
|
|
||||||
type fsError = Error & { code: string };
|
type fsError = Error & { code: string };
|
||||||
|
|
||||||
|
82
app/src/helpers/loggingHelper.ts
Normal file
82
app/src/helpers/loggingHelper.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// This helper allows logs to either be printed to the console as they would normally or if
|
||||||
|
// the USE_LOG_FILE environment variable is set (such as through our playwright tests), then
|
||||||
|
// the logs can be diverted from the command line to a log file, so that they can be displayed
|
||||||
|
// later (such as at the end of a playwright test run to help diagnose potential failures).
|
||||||
|
// Use this instead of loglevel whenever logging messages inside the app.
|
||||||
|
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import loglevel from 'loglevel';
|
||||||
|
|
||||||
|
import { safeGetEnv } from './playwrightHelpers';
|
||||||
|
|
||||||
|
const USE_LOG_FILE = safeGetEnv('USE_LOG_FILE') === '1';
|
||||||
|
const LOG_FILE_DIR = safeGetEnv('LOG_FILE_DIR') ?? process.cwd();
|
||||||
|
const LOG_FILENAME = path.join(LOG_FILE_DIR, `${new Date().getTime()}.log`);
|
||||||
|
|
||||||
|
const logLevelNames = ['TRACE', 'DEBUG', 'INFO ', 'WARN ', 'ERROR'];
|
||||||
|
|
||||||
|
function _logger(
|
||||||
|
logFunc: (...args: unknown[]) => void,
|
||||||
|
level: loglevel.LogLevelNumbers,
|
||||||
|
...args: unknown[]
|
||||||
|
): void {
|
||||||
|
if (USE_LOG_FILE && loglevel.getLevel() >= level) {
|
||||||
|
for (const arg of args) {
|
||||||
|
try {
|
||||||
|
const lines =
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
|
JSON.stringify(arg, null, 2)?.split('\n') ?? `${arg}`.split('\n');
|
||||||
|
for (const line of lines) {
|
||||||
|
fs.appendFileSync(
|
||||||
|
LOG_FILENAME,
|
||||||
|
`${new Date().getTime()} ${logLevelNames[level]} ${line}\n`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
|
fs.appendFileSync(LOG_FILENAME, `${logLevelNames[level]} ${arg}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logFunc(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debug(...args: unknown[]): void {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
_logger(loglevel.debug, loglevel.levels.DEBUG, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function error(...args: unknown[]): void {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
_logger(loglevel.error, loglevel.levels.ERROR, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function info(...args: unknown[]): void {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
_logger(loglevel.info, loglevel.levels.INFO, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function log(...args: unknown[]): void {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
_logger(loglevel.info, loglevel.levels.INFO, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLevel(
|
||||||
|
level: loglevel.LogLevelDesc,
|
||||||
|
persist?: boolean,
|
||||||
|
): void {
|
||||||
|
loglevel.setLevel(level, persist);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trace(...args: unknown[]): void {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
_logger(loglevel.trace, loglevel.levels.TRACE, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function warn(...args: unknown[]): void {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
_logger(loglevel.warn, loglevel.levels.WARN, ...args);
|
||||||
|
}
|
6
app/src/helpers/playwrightHelpers.ts
Normal file
6
app/src/helpers/playwrightHelpers.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export const IS_PLAYWRIGHT = safeGetEnv('PLAYWRIGHT_TEST') === '1';
|
||||||
|
export const PLAYWRIGHT_CONFIG = safeGetEnv('PLAYWRIGHT_CONFIG');
|
||||||
|
|
||||||
|
export function safeGetEnv(key: string): string | undefined {
|
||||||
|
return key in process.env ? process.env[key] : undefined;
|
||||||
|
}
|
@ -5,10 +5,9 @@ import {
|
|||||||
NewWindowWebContentsEvent,
|
NewWindowWebContentsEvent,
|
||||||
WebContents,
|
WebContents,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
import log from 'loglevel';
|
|
||||||
import { WindowOptions } from '../../../shared/src/options/model';
|
|
||||||
|
|
||||||
import { linkIsInternal, nativeTabsSupported, openExternal } from './helpers';
|
import { linkIsInternal, nativeTabsSupported, openExternal } from './helpers';
|
||||||
|
import * as log from './loggingHelper';
|
||||||
import {
|
import {
|
||||||
blockExternalURL,
|
blockExternalURL,
|
||||||
createAboutBlankWindow,
|
createAboutBlankWindow,
|
||||||
@ -17,6 +16,7 @@ import {
|
|||||||
sendParamsOnDidFinishLoad,
|
sendParamsOnDidFinishLoad,
|
||||||
setProxyRules,
|
setProxyRules,
|
||||||
} from './windowHelpers';
|
} from './windowHelpers';
|
||||||
|
import { WindowOptions } from '../../../shared/src/options/model';
|
||||||
|
|
||||||
export function onNewWindow(
|
export function onNewWindow(
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
dialog,
|
dialog,
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
@ -8,10 +10,9 @@ import {
|
|||||||
OnResponseStartedListenerDetails,
|
OnResponseStartedListenerDetails,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
|
|
||||||
import log from 'loglevel';
|
|
||||||
import path from 'path';
|
|
||||||
import { TrayValue, WindowOptions } from '../../../shared/src/options/model';
|
|
||||||
import { getCSSToInject, isOSX, nativeTabsSupported } from './helpers';
|
import { getCSSToInject, isOSX, nativeTabsSupported } from './helpers';
|
||||||
|
import * as log from './loggingHelper';
|
||||||
|
import { TrayValue, WindowOptions } from '../../../shared/src/options/model';
|
||||||
|
|
||||||
const ZOOM_INTERVAL = 0.1;
|
const ZOOM_INTERVAL = 0.1;
|
||||||
|
|
||||||
|
@ -12,17 +12,22 @@ import electron, {
|
|||||||
Event,
|
Event,
|
||||||
} from 'electron';
|
} from 'electron';
|
||||||
import electronDownload from 'electron-dl';
|
import electronDownload from 'electron-dl';
|
||||||
import * as log from 'loglevel';
|
|
||||||
|
|
||||||
import { createLoginWindow } from './components/loginWindow';
|
import { createLoginWindow } from './components/loginWindow';
|
||||||
import {
|
import {
|
||||||
|
createMainWindow,
|
||||||
saveAppArgs,
|
saveAppArgs,
|
||||||
APP_ARGS_FILE_PATH,
|
APP_ARGS_FILE_PATH,
|
||||||
createMainWindow,
|
|
||||||
} from './components/mainWindow';
|
} from './components/mainWindow';
|
||||||
import { createTrayIcon } from './components/trayIcon';
|
import { createTrayIcon } from './components/trayIcon';
|
||||||
import { isOSX, removeUserAgentSpecifics } from './helpers/helpers';
|
import { isOSX, removeUserAgentSpecifics } from './helpers/helpers';
|
||||||
import { inferFlashPath } from './helpers/inferFlash';
|
import { inferFlashPath } from './helpers/inferFlash';
|
||||||
|
import * as log from './helpers/loggingHelper';
|
||||||
|
import {
|
||||||
|
IS_PLAYWRIGHT,
|
||||||
|
PLAYWRIGHT_CONFIG,
|
||||||
|
safeGetEnv,
|
||||||
|
} from './helpers/playwrightHelpers';
|
||||||
import { setupNativefierWindow } from './helpers/windowEvents';
|
import { setupNativefierWindow } from './helpers/windowEvents';
|
||||||
import {
|
import {
|
||||||
OutputOptions,
|
OutputOptions,
|
||||||
@ -34,17 +39,21 @@ if (require('electron-squirrel-startup')) {
|
|||||||
app.exit();
|
app.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.argv.indexOf('--verbose') > -1) {
|
if (process.argv.indexOf('--verbose') > -1 || safeGetEnv('VERBOSE') === '1') {
|
||||||
log.setLevel('DEBUG');
|
log.setLevel('DEBUG');
|
||||||
process.traceDeprecation = true;
|
process.traceDeprecation = true;
|
||||||
process.traceProcessWarnings = true;
|
process.traceProcessWarnings = true;
|
||||||
|
process.argv.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mainWindow: BrowserWindow;
|
let mainWindow: BrowserWindow;
|
||||||
|
|
||||||
const appArgs = JSON.parse(
|
const appArgs =
|
||||||
fs.readFileSync(APP_ARGS_FILE_PATH, 'utf8'),
|
IS_PLAYWRIGHT && PLAYWRIGHT_CONFIG
|
||||||
) as OutputOptions;
|
? (JSON.parse(PLAYWRIGHT_CONFIG) as OutputOptions)
|
||||||
|
: (JSON.parse(
|
||||||
|
fs.readFileSync(APP_ARGS_FILE_PATH, 'utf8'),
|
||||||
|
) as OutputOptions);
|
||||||
|
|
||||||
log.debug('appArgs', appArgs);
|
log.debug('appArgs', appArgs);
|
||||||
// Do this relatively early so that we can start storing appData with the app
|
// Do this relatively early so that we can start storing appData with the app
|
||||||
@ -182,7 +191,7 @@ const setDockBadge = isOSX()
|
|||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
log.debug('app.window-all-closed');
|
log.debug('app.window-all-closed');
|
||||||
if (!isOSX() || appArgs.fastQuit) {
|
if (!isOSX() || appArgs.fastQuit || IS_PLAYWRIGHT) {
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -243,7 +252,7 @@ if (appArgs.widevine) {
|
|||||||
|
|
||||||
app.on('activate', (event: electron.Event, hasVisibleWindows: boolean) => {
|
app.on('activate', (event: electron.Event, hasVisibleWindows: boolean) => {
|
||||||
log.debug('app.activate', { event, hasVisibleWindows });
|
log.debug('app.activate', { event, hasVisibleWindows });
|
||||||
if (isOSX()) {
|
if (isOSX() && !IS_PLAYWRIGHT) {
|
||||||
// this is called when the dock is clicked
|
// this is called when the dock is clicked
|
||||||
if (!hasVisibleWindows) {
|
if (!hasVisibleWindows) {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
|
1286
npm-shrinkwrap.json
generated
1286
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@ -46,6 +46,7 @@
|
|||||||
"relock": "rm -rf ./node_modules/ ./app/node_modules/ ./npm-shrinkwrap.json ./app/npm-shrinkwrap.json && npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out; cd app && npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out",
|
"relock": "rm -rf ./node_modules/ ./app/node_modules/ ./npm-shrinkwrap.json ./app/npm-shrinkwrap.json && npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out; cd app && npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out",
|
||||||
"test:integration": "jest --testRegex '.*integration-test.js'",
|
"test:integration": "jest --testRegex '.*integration-test.js'",
|
||||||
"test:manual": "npm run build && bash .github/manual-test",
|
"test:manual": "npm run build && bash .github/manual-test",
|
||||||
|
"test:playwright": "jest --testRegex '.*playwright-test.js'",
|
||||||
"test:unit": "jest",
|
"test:unit": "jest",
|
||||||
"test:watch": "echo 'Remember to run npm run build:watch for the test watcher to work!' && jest --watchAll --collectCoverage=false",
|
"test:watch": "echo 'Remember to run npm run build:watch for the test watcher to work!' && jest --watchAll --collectCoverage=false",
|
||||||
"test:withlog": "LOGLEVEL=trace npm run test",
|
"test:withlog": "LOGLEVEL=trace npm run test",
|
||||||
@ -78,10 +79,12 @@
|
|||||||
"@types/tmp": "^0.2.1",
|
"@types/tmp": "^0.2.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||||
"@typescript-eslint/parser": "^5.3.0",
|
"@typescript-eslint/parser": "^5.3.0",
|
||||||
|
"electron": "^18.0.3",
|
||||||
"eslint": "^8.1.0",
|
"eslint": "^8.1.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"jest": "^27.0.6",
|
"jest": "^27.0.6",
|
||||||
|
"playwright": "^1.20.1",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-loader": "^9.2.3",
|
"ts-loader": "^9.2.3",
|
||||||
@ -95,6 +98,14 @@
|
|||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"collectCoverage": true,
|
"collectCoverage": true,
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"./app/dist/**/*.js",
|
||||||
|
"./lib/**/*.js",
|
||||||
|
"./shared/lib/**/*.js"
|
||||||
|
],
|
||||||
|
"coveragePathIgnorePatterns": [
|
||||||
|
"[.-]test.js$"
|
||||||
|
],
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
"^electron$": "<rootDir>/app/dist/mocks/electron.js"
|
"^electron$": "<rootDir>/app/dist/mocks/electron.js"
|
||||||
},
|
},
|
||||||
@ -103,19 +114,18 @@
|
|||||||
],
|
],
|
||||||
"testEnvironment": "node",
|
"testEnvironment": "node",
|
||||||
"testPathIgnorePatterns": [
|
"testPathIgnorePatterns": [
|
||||||
"<rootDir>/src.*",
|
"<rootDir>/app/node_modules.*",
|
||||||
"<rootDir>/node_modules.*",
|
|
||||||
"<rootDir>/app/src.*",
|
"<rootDir>/app/src.*",
|
||||||
"<rootDir>/app/lib.*",
|
"<rootDir>/app/lib.*",
|
||||||
"<rootDir>/app/node_modules.*"
|
"<rootDir>/src.*"
|
||||||
],
|
],
|
||||||
"watchPathIgnorePatterns": [
|
"watchPathIgnorePatterns": [
|
||||||
"<rootDir>/src.*",
|
|
||||||
"<rootDir>/tsconfig-base.json",
|
|
||||||
"<rootDir>/app/src.*",
|
|
||||||
"<rootDir>/app/lib.*",
|
"<rootDir>/app/lib.*",
|
||||||
|
"<rootDir>/app/src.*",
|
||||||
"<rootDir>/app/tsconfig.json",
|
"<rootDir>/app/tsconfig.json",
|
||||||
"<rootDir>/shared/tsconfig.json"
|
"<rootDir>/shared/tsconfig.json",
|
||||||
|
"<rootDir>/src.*",
|
||||||
|
"<rootDir>/tsconfig-base.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
|
@ -3,6 +3,7 @@ import * as path from 'path';
|
|||||||
export const DEFAULT_APP_NAME = 'APP';
|
export const DEFAULT_APP_NAME = 'APP';
|
||||||
|
|
||||||
// Update both DEFAULT_ELECTRON_VERSION and DEFAULT_CHROME_VERSION together,
|
// Update both DEFAULT_ELECTRON_VERSION and DEFAULT_CHROME_VERSION together,
|
||||||
|
// and update package.json / devDeps / electron to value of DEFAULT_ELECTRON_VERSION
|
||||||
// and update app / package.json / devDeps / electron to value of DEFAULT_ELECTRON_VERSION
|
// and update app / package.json / devDeps / electron to value of DEFAULT_ELECTRON_VERSION
|
||||||
export const DEFAULT_ELECTRON_VERSION = '18.0.3';
|
export const DEFAULT_ELECTRON_VERSION = '18.0.3';
|
||||||
// https://atom.io/download/atom-shell/index.json
|
// https://atom.io/download/atom-shell/index.json
|
||||||
|
@ -5,3 +5,5 @@ if (process.env.LOGLEVEL) {
|
|||||||
} else {
|
} else {
|
||||||
log.disableAll();
|
log.disableAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process.traceDeprecation = true;
|
||||||
|
401
src/playwright-test.ts
Normal file
401
src/playwright-test.ts
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
import { once } from 'events';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import { Shell } from 'electron';
|
||||||
|
import {
|
||||||
|
_electron,
|
||||||
|
ConsoleMessage,
|
||||||
|
Dialog,
|
||||||
|
ElectronApplication,
|
||||||
|
Page,
|
||||||
|
} from 'playwright';
|
||||||
|
|
||||||
|
import { getTempDir } from './helpers/helpers';
|
||||||
|
import { NativefierOptions } from '../shared/src/options/model';
|
||||||
|
|
||||||
|
const INJECT_DIR = path.join(__dirname, '..', 'app', 'inject');
|
||||||
|
|
||||||
|
const log = console;
|
||||||
|
|
||||||
|
function sleep(milliseconds: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, milliseconds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Application launch', () => {
|
||||||
|
jest.setTimeout(60000);
|
||||||
|
|
||||||
|
let app: ElectronApplication;
|
||||||
|
let appClosed = true;
|
||||||
|
|
||||||
|
const appMainJSPath = path.join(__dirname, '..', 'app', 'lib', 'main.js');
|
||||||
|
const DEFAULT_CONFIG: NativefierOptions = {
|
||||||
|
targetUrl: 'https://npmjs.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
const logFileDir = getTempDir('playwright');
|
||||||
|
|
||||||
|
const metaOrAlt = process.platform === 'darwin' ? 'Meta' : 'Alt';
|
||||||
|
const metaOrCtrl = process.platform === 'darwin' ? 'Meta' : 'Control';
|
||||||
|
|
||||||
|
const spawnApp = async (
|
||||||
|
playwrightConfig: NativefierOptions = { ...DEFAULT_CONFIG },
|
||||||
|
awaitFirstWindow = true,
|
||||||
|
preventNavigation = false,
|
||||||
|
): Promise<Page | undefined> => {
|
||||||
|
const consoleListener = (consoleMessage: ConsoleMessage): void => {
|
||||||
|
const consoleMethods: Record<string, (...args: unknown[]) => unknown> = {
|
||||||
|
debug: log.debug.bind(console),
|
||||||
|
error: log.error.bind(console),
|
||||||
|
info: log.info.bind(console),
|
||||||
|
log: log.log.bind(console),
|
||||||
|
trace: log.trace.bind(console),
|
||||||
|
warn: log.warn.bind(console),
|
||||||
|
};
|
||||||
|
Promise.all(consoleMessage.args().map((x) => x.jsonValue()))
|
||||||
|
.then((args) => {
|
||||||
|
if (consoleMessage.type() in consoleMethods) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
consoleMethods[consoleMessage.type()]('window.console', args);
|
||||||
|
} else {
|
||||||
|
log.log('window.console', args);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => log.log('window.console', consoleMessage));
|
||||||
|
};
|
||||||
|
app = await _electron.launch({
|
||||||
|
args: [appMainJSPath],
|
||||||
|
env: {
|
||||||
|
LOG_FILE_DIR: logFileDir,
|
||||||
|
PLAYWRIGHT_TEST: '1',
|
||||||
|
PLAYWRIGHT_CONFIG: JSON.stringify(playwrightConfig),
|
||||||
|
USE_LOG_FILE: '1',
|
||||||
|
VERBOSE: '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
app.on('window', (page: Page) => {
|
||||||
|
page.on('console', consoleListener);
|
||||||
|
if (preventNavigation) {
|
||||||
|
// Prevent page navigation so we can have a reliable test
|
||||||
|
page
|
||||||
|
.route('*', (route): void => {
|
||||||
|
log.info(`Preventing route: ${route.request().url()}`);
|
||||||
|
route.abort().catch((error) => {
|
||||||
|
log.error('ERROR', error);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
log.error('ERROR', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
app.on('close', () => (appClosed = true));
|
||||||
|
appClosed = false;
|
||||||
|
if (!awaitFirstWindow) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const window = await app.firstWindow();
|
||||||
|
// Wait for our initial page to finish loading, otherwise some tests will break
|
||||||
|
let waited = 0;
|
||||||
|
while (
|
||||||
|
window.url() === 'about:blank' &&
|
||||||
|
playwrightConfig.targetUrl !== 'about:blank' &&
|
||||||
|
waited < 2000
|
||||||
|
) {
|
||||||
|
waited += 100;
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
return window;
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
nukeInjects();
|
||||||
|
nukeLogs(logFileDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
if (app && !appClosed) {
|
||||||
|
await app.close();
|
||||||
|
}
|
||||||
|
if (process.env.DEBUG) {
|
||||||
|
showLogs(logFileDir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shows an initial window', async () => {
|
||||||
|
const mainWindow = (await spawnApp()) as Page;
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
expect(app.windows()).toHaveLength(1);
|
||||||
|
expect(await mainWindow.title()).toBe('npm');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can inject some CSS', async () => {
|
||||||
|
const fuschia = 'rgb(255, 0, 255)';
|
||||||
|
createInject(
|
||||||
|
'inject.css',
|
||||||
|
`* { background-color: ${fuschia} !important; }`,
|
||||||
|
);
|
||||||
|
const mainWindow = (await spawnApp()) as Page;
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
const headerStyle = await mainWindow.$eval('header', (el) =>
|
||||||
|
window.getComputedStyle(el),
|
||||||
|
);
|
||||||
|
expect(headerStyle.backgroundColor).toBe(fuschia);
|
||||||
|
|
||||||
|
await mainWindow.click('#nav-products-link');
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
const headerStylePostNavigate = await mainWindow.$eval('header', (el) =>
|
||||||
|
window.getComputedStyle(el),
|
||||||
|
);
|
||||||
|
expect(headerStylePostNavigate.backgroundColor).toBe(fuschia);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can inject some JS', async () => {
|
||||||
|
const alertMsg = 'hello world from inject';
|
||||||
|
createInject(
|
||||||
|
'inject.js',
|
||||||
|
`setTimeout(() => {alert("${alertMsg}"); }, 5000);`, // Buy ourselves 5 seconds to get the dialog handler setup
|
||||||
|
);
|
||||||
|
const mainWindow = (await spawnApp(
|
||||||
|
{ ...DEFAULT_CONFIG },
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)) as Page;
|
||||||
|
const [dialogPromise] = (await once(
|
||||||
|
mainWindow,
|
||||||
|
'dialog',
|
||||||
|
)) as unknown as Promise<Dialog>[];
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const dialog: Dialog = await dialogPromise;
|
||||||
|
await dialog.dismiss();
|
||||||
|
expect(dialog.message()).toBe(alertMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can open internal links', async () => {
|
||||||
|
const mainWindow = (await spawnApp()) as Page;
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
await mainWindow.click('#nav-products-link');
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
expect(app.windows()).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tries to open external links', async () => {
|
||||||
|
const mainWindow = (await spawnApp()) as Page;
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
|
// Install the mock first
|
||||||
|
await app.evaluate(({ shell }: { shell: Shell }) => {
|
||||||
|
// @ts-expect-error injecting into shell so that this promise
|
||||||
|
// can be accessed outside of this anonymous function's scope
|
||||||
|
// Not my favorite thing to do, but I could not find another way
|
||||||
|
process.openExternalPromise = new Promise((resolve) => {
|
||||||
|
shell.openExternal = async (url: string): Promise<void> => {
|
||||||
|
resolve(url);
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click, but don't await it - Playwright waits for stuff that does not happen when Electron does openExternal.
|
||||||
|
mainWindow
|
||||||
|
.click('#footer > div:nth-child(2) > ul > li:nth-child(2) > a')
|
||||||
|
.catch((err: unknown) => {
|
||||||
|
expect(err).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go pull out our value returned by our hacky global promise
|
||||||
|
const openExternalUrl = await app.evaluate('process.openExternalPromise');
|
||||||
|
expect(openExternalUrl).not.toBe('https://www.npmjs.com/');
|
||||||
|
|
||||||
|
expect(openExternalUrl).not.toBe(DEFAULT_CONFIG.targetUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Currently disabled. Playwright doesn't seem to support app keypress events for menu shortcuts.
|
||||||
|
// Will enable when https://github.com/microsoft/playwright/issues/8004 is resolved.
|
||||||
|
test.skip('keyboard shortcuts: zoom', async () => {
|
||||||
|
const mainWindow = (await spawnApp()) as Page;
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
|
const defaultZoom: number | undefined = await app.evaluate(
|
||||||
|
({ BrowserWindow }) =>
|
||||||
|
BrowserWindow.getFocusedWindow()?.webContents?.zoomFactor,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(defaultZoom).toBeDefined();
|
||||||
|
|
||||||
|
await mainWindow.keyboard.press(`${metaOrCtrl}+Equal`);
|
||||||
|
const postZoomIn = await app.evaluate(
|
||||||
|
({ BrowserWindow }): number | undefined =>
|
||||||
|
BrowserWindow.getFocusedWindow()?.webContents?.zoomFactor,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(postZoomIn).toBeGreaterThan(defaultZoom as number);
|
||||||
|
|
||||||
|
await mainWindow.keyboard.press(`${metaOrCtrl}+0`);
|
||||||
|
const postZoomReset = await app.evaluate(
|
||||||
|
({ BrowserWindow }): number | undefined =>
|
||||||
|
BrowserWindow.getFocusedWindow()?.webContents?.zoomFactor,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(postZoomReset).toEqual(defaultZoom);
|
||||||
|
|
||||||
|
await mainWindow.keyboard.press(`${metaOrCtrl}+Minus`);
|
||||||
|
const postZoomOut: number | undefined = await app.evaluate(
|
||||||
|
({ BrowserWindow }) =>
|
||||||
|
BrowserWindow.getFocusedWindow()?.webContents?.zoomFactor,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(postZoomOut).toBeLessThan(defaultZoom as number);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Currently disabled. Playwright doesn't seem to support app keypress events for menu shortcuts.
|
||||||
|
// Will enable when https://github.com/microsoft/playwright/issues/8004 is resolved.
|
||||||
|
test.skip('keyboard shortcuts: back and forward', async () => {
|
||||||
|
const mainWindow = (await spawnApp()) as Page;
|
||||||
|
await mainWindow.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
mainWindow.click('#nav-products-link'),
|
||||||
|
mainWindow.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Go back
|
||||||
|
// console.log(`${metaOrAlt}+ArrowLeft`);
|
||||||
|
await mainWindow.keyboard.press(`${metaOrAlt}+ArrowLeft`);
|
||||||
|
await mainWindow.waitForNavigation({ waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
|
const backUrl = await mainWindow.evaluate(() => window.location.href);
|
||||||
|
|
||||||
|
expect(backUrl).toBe(DEFAULT_CONFIG.targetUrl);
|
||||||
|
|
||||||
|
// Go forward
|
||||||
|
// console.log(`${metaOrAlt}+ArrowRight`);
|
||||||
|
await mainWindow.keyboard.press(`${metaOrAlt}+ArrowRight`);
|
||||||
|
await mainWindow.waitForNavigation({ waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
|
const forwardUrl = await mainWindow.evaluate(() => window.location.href);
|
||||||
|
|
||||||
|
expect(forwardUrl).not.toBe(DEFAULT_CONFIG.targetUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('no errors thrown in console', async () => {
|
||||||
|
await spawnApp({ ...DEFAULT_CONFIG }, false);
|
||||||
|
const mainWindow = await app.firstWindow();
|
||||||
|
mainWindow.addListener('console', (consoleMessage: ConsoleMessage) => {
|
||||||
|
try {
|
||||||
|
expect(consoleMessage.type()).not.toBe('error');
|
||||||
|
} catch {
|
||||||
|
// Do it this way so we'll see the whole message, not just
|
||||||
|
// expect('error').not.toBe('error')
|
||||||
|
// which isn't particularly useful
|
||||||
|
expect({
|
||||||
|
message: 'console.error called unexpectedly with',
|
||||||
|
consoleMessage,
|
||||||
|
}).toBeUndefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Give the app 5 seconds to spin up and ensure no errors happened
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('basic auth', async () => {
|
||||||
|
const mainWindow = (await spawnApp({
|
||||||
|
targetUrl: 'http://httpbin.org/basic-auth/foo/bar',
|
||||||
|
basicAuthUsername: 'foo',
|
||||||
|
basicAuthPassword: 'bar',
|
||||||
|
})) as Page;
|
||||||
|
await mainWindow.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
const documentText = await mainWindow.evaluate<string>(
|
||||||
|
'document.documentElement.innerText',
|
||||||
|
);
|
||||||
|
|
||||||
|
const documentJSON = JSON.parse(documentText) as {
|
||||||
|
authenticated: boolean;
|
||||||
|
user: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(documentJSON).toEqual({
|
||||||
|
authenticated: true,
|
||||||
|
user: 'foo',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('basic auth without pre-providing', async () => {
|
||||||
|
const mainWindow = (await spawnApp({
|
||||||
|
targetUrl: 'http://httpbin.org/basic-auth/foo/bar',
|
||||||
|
})) as Page;
|
||||||
|
await mainWindow.waitForLoadState('load');
|
||||||
|
|
||||||
|
// Give the app a few seconds to open the login window
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
|
const appWindows = app.windows();
|
||||||
|
|
||||||
|
expect(appWindows).toHaveLength(2);
|
||||||
|
|
||||||
|
const loginWindow = appWindows.filter((x) => x !== mainWindow)[0];
|
||||||
|
|
||||||
|
await loginWindow.waitForLoadState('domcontentloaded');
|
||||||
|
|
||||||
|
const usernameField = await loginWindow.$('#username-input');
|
||||||
|
|
||||||
|
expect(usernameField).not.toBeNull();
|
||||||
|
|
||||||
|
const passwordField = await loginWindow.$('#password-input');
|
||||||
|
|
||||||
|
expect(passwordField).not.toBeNull();
|
||||||
|
|
||||||
|
const submitButton = await loginWindow.$('#submit-form-button');
|
||||||
|
|
||||||
|
expect(submitButton).not.toBeNull();
|
||||||
|
|
||||||
|
await usernameField?.fill('foo');
|
||||||
|
await passwordField?.fill('bar');
|
||||||
|
await submitButton?.click();
|
||||||
|
|
||||||
|
await mainWindow.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
const documentText = await mainWindow.evaluate<string>(
|
||||||
|
'document.documentElement.innerText',
|
||||||
|
);
|
||||||
|
|
||||||
|
const documentJSON = JSON.parse(documentText) as {
|
||||||
|
authenticated: boolean;
|
||||||
|
user: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(documentJSON).toEqual({
|
||||||
|
authenticated: true,
|
||||||
|
user: 'foo',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createInject(filename: string, contents: string): void {
|
||||||
|
fs.writeFileSync(path.join(INJECT_DIR, filename), contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nukeInjects(): void {
|
||||||
|
if (!fs.existsSync(INJECT_DIR)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const injected = fs
|
||||||
|
.readdirSync(INJECT_DIR)
|
||||||
|
.filter((x) => x !== '_placeholder');
|
||||||
|
injected.forEach((x) => fs.unlinkSync(path.join(INJECT_DIR, x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function nukeLogs(logFileDir: string): void {
|
||||||
|
const logs = fs.readdirSync(logFileDir).filter((x) => x.endsWith('.log'));
|
||||||
|
logs.forEach((x) => fs.unlinkSync(path.join(logFileDir, x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function showLogs(logFileDir: string): void {
|
||||||
|
const logs = fs.readdirSync(logFileDir).filter((x) => x.endsWith('.log'));
|
||||||
|
for (const logFile of logs) {
|
||||||
|
log.log(fs.readFileSync(path.join(logFileDir, logFile)).toString());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user