presidium 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/GoogleChromeDevTools.js +112 -20
  2. package/{internal/GoogleChromeForTesting.js → GoogleChromeForTesting.js} +126 -24
  3. package/README.md +6 -2
  4. package/index.js +1 -0
  5. package/package.json +2 -1
  6. package/internal/GoogleChromeForTesting.test.js +0 -50
  7. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-69977DD3-9B6F.pma +0 -0
  8. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-69977DD4-9B86.pma +0 -0
  9. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-69978250-A191.pma +0 -0
  10. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-69978251-A1AD.pma +0 -0
  11. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-69978277-A1FA.pma +0 -0
  12. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-69978278-A209.pma +0 -0
  13. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-6997A286-BC0E.pma +0 -0
  14. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-6997A287-BC46.pma +0 -0
  15. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-6997A7D0-C1E4.pma +0 -0
  16. package/internal/tmp/chrome/BrowserMetrics/BrowserMetrics-6997A7D1-C1FE.pma +0 -0
  17. package/internal/tmp/chrome/ChromeFeatureState +0 -1
  18. package/internal/tmp/chrome/Default/Affiliation Database +0 -0
  19. package/internal/tmp/chrome/Default/Affiliation Database-journal +0 -0
  20. package/internal/tmp/chrome/Default/ClientCertificates/LOCK +0 -0
  21. package/internal/tmp/chrome/Default/ClientCertificates/LOG +0 -0
  22. package/internal/tmp/chrome/Default/ClientCertificates/LOG.old +0 -0
  23. package/internal/tmp/chrome/Default/Extension Rules/000003.log +0 -0
  24. package/internal/tmp/chrome/Default/Extension Rules/CURRENT +0 -1
  25. package/internal/tmp/chrome/Default/Extension Rules/LOCK +0 -0
  26. package/internal/tmp/chrome/Default/Extension Rules/LOG +0 -3
  27. package/internal/tmp/chrome/Default/Extension Rules/LOG.old +0 -3
  28. package/internal/tmp/chrome/Default/Extension Rules/MANIFEST-000001 +0 -0
  29. package/internal/tmp/chrome/Default/Extension Scripts/000003.log +0 -0
  30. package/internal/tmp/chrome/Default/Extension Scripts/CURRENT +0 -1
  31. package/internal/tmp/chrome/Default/Extension Scripts/LOCK +0 -0
  32. package/internal/tmp/chrome/Default/Extension Scripts/LOG +0 -3
  33. package/internal/tmp/chrome/Default/Extension Scripts/LOG.old +0 -3
  34. package/internal/tmp/chrome/Default/Extension Scripts/MANIFEST-000001 +0 -0
  35. package/internal/tmp/chrome/Default/Favicons +0 -0
  36. package/internal/tmp/chrome/Default/Favicons-journal +0 -0
  37. package/internal/tmp/chrome/Default/History +0 -0
  38. package/internal/tmp/chrome/Default/History-journal +0 -0
  39. package/internal/tmp/chrome/Default/LOCK +0 -0
  40. package/internal/tmp/chrome/Default/LOG +0 -0
  41. package/internal/tmp/chrome/Default/LOG.old +0 -0
  42. package/internal/tmp/chrome/Default/Local Storage/leveldb/000003.log +0 -0
  43. package/internal/tmp/chrome/Default/Local Storage/leveldb/CURRENT +0 -1
  44. package/internal/tmp/chrome/Default/Local Storage/leveldb/LOCK +0 -0
  45. package/internal/tmp/chrome/Default/Local Storage/leveldb/LOG.old +0 -2
  46. package/internal/tmp/chrome/Default/Local Storage/leveldb/MANIFEST-000001 +0 -0
  47. package/internal/tmp/chrome/Default/PersistentOriginTrials/LOCK +0 -0
  48. package/internal/tmp/chrome/Default/PersistentOriginTrials/LOG +0 -0
  49. package/internal/tmp/chrome/Default/PersistentOriginTrials/LOG.old +0 -0
  50. package/internal/tmp/chrome/Default/README +0 -1
  51. package/internal/tmp/chrome/Default/Segmentation Platform/SegmentInfoDB/LOCK +0 -0
  52. package/internal/tmp/chrome/Default/Segmentation Platform/SegmentInfoDB/LOG +0 -0
  53. package/internal/tmp/chrome/Default/Segmentation Platform/SegmentInfoDB/LOG.old +0 -0
  54. package/internal/tmp/chrome/Default/Segmentation Platform/SignalDB/LOCK +0 -0
  55. package/internal/tmp/chrome/Default/Segmentation Platform/SignalDB/LOG +0 -0
  56. package/internal/tmp/chrome/Default/Segmentation Platform/SignalDB/LOG.old +0 -0
  57. package/internal/tmp/chrome/Default/Segmentation Platform/SignalStorageConfigDB/LOCK +0 -0
  58. package/internal/tmp/chrome/Default/Segmentation Platform/SignalStorageConfigDB/LOG +0 -0
  59. package/internal/tmp/chrome/Default/Segmentation Platform/SignalStorageConfigDB/LOG.old +0 -0
  60. package/internal/tmp/chrome/Default/ServerCertificate +0 -0
  61. package/internal/tmp/chrome/Default/ServerCertificate-journal +0 -0
  62. package/internal/tmp/chrome/Default/Site Characteristics Database/000003.log +0 -0
  63. package/internal/tmp/chrome/Default/Site Characteristics Database/CURRENT +0 -1
  64. package/internal/tmp/chrome/Default/Site Characteristics Database/LOCK +0 -0
  65. package/internal/tmp/chrome/Default/Site Characteristics Database/LOG +0 -3
  66. package/internal/tmp/chrome/Default/Site Characteristics Database/LOG.old +0 -3
  67. package/internal/tmp/chrome/Default/Site Characteristics Database/MANIFEST-000001 +0 -0
  68. package/internal/tmp/chrome/Default/Sync Data/LevelDB/000003.log +0 -0
  69. package/internal/tmp/chrome/Default/Sync Data/LevelDB/CURRENT +0 -1
  70. package/internal/tmp/chrome/Default/Sync Data/LevelDB/LOCK +0 -0
  71. package/internal/tmp/chrome/Default/Sync Data/LevelDB/LOG +0 -3
  72. package/internal/tmp/chrome/Default/Sync Data/LevelDB/LOG.old +0 -3
  73. package/internal/tmp/chrome/Default/Sync Data/LevelDB/MANIFEST-000001 +0 -0
  74. package/internal/tmp/chrome/Default/chrome_cart_db/LOCK +0 -0
  75. package/internal/tmp/chrome/Default/chrome_cart_db/LOG +0 -0
  76. package/internal/tmp/chrome/Default/chrome_cart_db/LOG.old +0 -0
  77. package/internal/tmp/chrome/Default/commerce_subscription_db/LOCK +0 -0
  78. package/internal/tmp/chrome/Default/commerce_subscription_db/LOG +0 -0
  79. package/internal/tmp/chrome/Default/commerce_subscription_db/LOG.old +0 -0
  80. package/internal/tmp/chrome/Default/discount_infos_db/LOCK +0 -0
  81. package/internal/tmp/chrome/Default/discount_infos_db/LOG +0 -0
  82. package/internal/tmp/chrome/Default/discount_infos_db/LOG.old +0 -0
  83. package/internal/tmp/chrome/Default/discounts_db/LOCK +0 -0
  84. package/internal/tmp/chrome/Default/discounts_db/LOG +0 -0
  85. package/internal/tmp/chrome/Default/discounts_db/LOG.old +0 -0
  86. package/internal/tmp/chrome/Default/parcel_tracking_db/LOCK +0 -0
  87. package/internal/tmp/chrome/Default/parcel_tracking_db/LOG +0 -0
  88. package/internal/tmp/chrome/Default/parcel_tracking_db/LOG.old +0 -0
  89. package/internal/tmp/chrome/Default/power_bookmarks/PowerBookmarks.db +0 -0
  90. package/internal/tmp/chrome/Default/power_bookmarks/PowerBookmarks.db-journal +0 -0
  91. package/internal/tmp/chrome/First Run +0 -0
  92. package/internal/tmp/chrome/Last Version +0 -1
  93. package/internal/tmp/chrome/Local State +0 -1
  94. package/internal/tmp/chrome/ShaderCache/data_0 +0 -0
  95. package/internal/tmp/chrome/ShaderCache/data_1 +0 -0
  96. package/internal/tmp/chrome/ShaderCache/data_2 +0 -0
  97. package/internal/tmp/chrome/ShaderCache/data_3 +0 -0
  98. package/internal/tmp/chrome/ShaderCache/index +0 -0
  99. package/internal/tmp/chrome/Variations +0 -1
  100. package/internal/tmp/chrome/segmentation_platform/ukm_db +0 -0
  101. package/internal/tmp/chrome/segmentation_platform/ukm_db-wal +0 -0
@@ -1,5 +1,5 @@
1
1
  const EventEmitter = require('events')
2
- const GoogleChromeForTesting = require('./internal/GoogleChromeForTesting')
2
+ const GoogleChromeForTesting = require('./GoogleChromeForTesting')
3
3
  const WebSocket = require('./WebSocket')
4
4
 
5
5
  let id = 0
@@ -743,24 +743,40 @@ class GoogleChromeDevToolsRuntime {
743
743
  *
744
744
  * @docs
745
745
  * ```coffeescript [specscript]
746
+ * new GoogleChromeDevTools(
747
+ * googleChromeForTesting GoogleChromeForTesting
748
+ * ) -> googleChromeDevTools GoogleChromeDevTools
749
+ *
746
750
  * new GoogleChromeDevTools(options {
747
751
  * chromeVersion: 'stable'|'beta'|'dev'|'canary'|string,
752
+ * chromeDir: string,
753
+ * remoteDebuggingPort: number,
748
754
  * headless: boolean,
755
+ * userDataDir: string,
756
+ * useMockKeychain: boolean,
749
757
  * }) -> googleChromeDevTools GoogleChromeDevTools
750
758
  * ```
751
759
  *
752
760
  * Presidium GoogleChromeDevTools client for test automation.
753
761
  *
754
762
  * Arguments:
763
+ * * `googleChromeForTesting` - an instance of a Presidium [GoogleChromeForTesting](/docs/GoogleChromeForTesting) client.
755
764
  * * `options`
756
- * * `chromeVersion` - the version of Google Chrome for Testing to download.
757
- * * `headless` - whether to run Google Chrome for Testing in headless mode.
765
+ * * `chromeVersion` - the version of Google Chrome for Testing to download. Defaults to `'stable'`.
766
+ * * `chromeDir` - the directory that Google Chrome for Testing will install to. Defaults to ``google-chrome-for-testing'`.
767
+ * * `remoteDebuggingPort` - the port that the Chrome DevTools Protocol server will listen on. Defaults to `9222`
768
+ * * `headless` - whether to run Google Chrome for Testing in headless mode. Defaults to `false`.
769
+ * * `userDataDir` - directory for storing user profile data such as history, bookmarks, cookies, and settings. Defaults to `tmp/chrome`.
770
+ * * `useMockKeychain` - whether to use a mock keychain instead of the system's real security keychain. Defaults to `true`.
758
771
  *
759
772
  * Returns:
760
- * * `googleChromeDevTools` - an instance of the `GoogleChromeDevTools` client.
773
+ * * `googleChromeDevTools` - an instance of the Presidium GoogleChromeDevTools client.
761
774
  *
762
775
  * ```javascript
763
- * const googleChromeDevTools = new GoogleChromeDevTools()
776
+ * const googleChromeForTesting = new GoogleChromeForTesting()
777
+ * await googleChromeForTesting.init()
778
+ *
779
+ * const googleChromeDevTools = new GoogleChromeDevTools(googleChromeForTesting)
764
780
  * await googleChromeDevTools.init()
765
781
  *
766
782
  * const target = await googleChromeDevTools.Target.getTargets()
@@ -802,7 +818,10 @@ class GoogleChromeDevToolsRuntime {
802
818
  * Every Chrome DevTools Protocol client needs to first attach to the target using the `Target.attachToTarget` command. The command will establish a protocol session with the given target and return a `sessionId`. The returned `sessionId` should be set on the `GoogleChromeDevTools` client using [`setSessionId`](#setSessionId) or included in every message to the DevTools server.
803
819
  *
804
820
  * ```javascript
805
- * const googleChromeDevTools = new GoogleChromeDevTools()
821
+ * const googleChromeForTesting = new GoogleChromeForTesting()
822
+ * await googleChromeForTesting.init()
823
+ *
824
+ * const googleChromeDevTools = new GoogleChromeDevTools(googleChromeForTesting)
806
825
  * await googleChromeDevTools.init()
807
826
  *
808
827
  * // get targets
@@ -823,26 +842,42 @@ class GoogleChromeDevToolsRuntime {
823
842
  * })
824
843
  * ```
825
844
  *
826
- * Install dependencies for Amazon Linux 2023:
827
- * ```sh
828
- * sudo dnf install -y cairo pango nss nspr atk at-spi2-atk cups-libs libdrm libxkbcommon libXcomposite libXdamage libXfixes libXrandr mesa-libgbm alsa-lib
829
- * ```
845
+ * References:
846
+ * * [Getting Started with the Chrome Devtools Protocol](https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md)
847
+ * * [Chrome Devtools Protocol](https://chromedevtools.github.io/devtools-protocol/)
830
848
  *
831
849
  * Supported platforms:
832
850
  * * `mac-arm64`
833
851
  * * `linux64`
852
+ * * `win64`
834
853
  *
835
- * References:
836
- * * [Getting Started with the Chrome Devtools Protocol](https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md)
837
- * * [Chrome Devtools Protocol](https://chromedevtools.github.io/devtools-protocol/)
854
+ * ## Further Installation
855
+ * Some further installation may be required for Linux platforms.
856
+ *
857
+ * ### Install headless dependencies for Amazon Linux 2023 / Red Hat
858
+ * ```sh
859
+ * sudo dnf install -y cairo pango nss nspr atk at-spi2-atk cups-libs libdrm libxkbcommon libXcomposite libXdamage libXfixes libXrandr mesa-libgbm alsa-lib
860
+ * ```
838
861
  *
862
+ * ### Install headless dependencies for Ubuntu / Debian
863
+ * ```sh
864
+ * sudo apt-get update && sudo apt-get install -y libcairo2 libpango-1.0-0 libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libatspi2.0-0 libcups2 libdrm-dev libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm-dev libasound2-dev
865
+ * ```
839
866
  */
840
867
  class GoogleChromeDevTools extends EventEmitter {
841
868
  constructor(options = {}) {
842
869
  super()
843
870
 
871
+ if (options.constructor == GoogleChromeForTesting) {
872
+ this.googleChromeForTesting = options
873
+ }
874
+
844
875
  this.chromeVersion = options.chromeVersion ?? 'stable'
876
+ this.chromeDir = options.chromeDir ?? 'google-chrome-for-testing'
877
+ this.remoteDebuggingPort = options.remoteDebuggingPort ?? 9222
845
878
  this.headless = options.headless ?? false
879
+ this.userDataDir = options.userDataDir ?? 'tmp/chrome'
880
+ this.useMockKeychain = options.useMockKeychain ?? true
846
881
  }
847
882
 
848
883
  /**
@@ -868,20 +903,25 @@ class GoogleChromeDevTools extends EventEmitter {
868
903
  * ```
869
904
  */
870
905
  async init() {
871
- const googleChromeForTesting = new GoogleChromeForTesting({
906
+ this.googleChromeForTesting ??= new GoogleChromeForTesting({
872
907
  chromeVersion: this.chromeVersion,
873
- userDataDir: `${__dirname}/tmp/chrome`,
874
- useMockKeychain: true,
908
+ chromeDir: this.chromeDir,
909
+ remoteDebuggingPort: this.remoteDebuggingPort,
875
910
  headless: this.headless,
911
+ userDataDir: this.userDataDir,
912
+ useMockKeychain: this.useMockKeychain,
876
913
  })
877
- await googleChromeForTesting.init()
878
- this.googleChromeForTesting = googleChromeForTesting
914
+ await this.googleChromeForTesting.init()
879
915
 
880
- this.websocket = new WebSocket(googleChromeForTesting.devtoolsUrl, {
916
+ this.websocket = new WebSocket(this.googleChromeForTesting.devtoolsUrl, {
881
917
  offerPerMessageDeflate: false,
882
918
  })
883
919
  this.websocket.on('error', error => {
884
- throw error
920
+ if (this.closed && error.code == 'ECONNRESET') {
921
+ console.error('Reset after close:', error)
922
+ } else {
923
+ throw error
924
+ }
885
925
  })
886
926
 
887
927
  this.websocket.on('message', message => {
@@ -934,6 +974,58 @@ class GoogleChromeDevTools extends EventEmitter {
934
974
  this.Runtime.sessionId = sessionId
935
975
  }
936
976
 
977
+ /**
978
+ * @name close
979
+ *
980
+ * @docs
981
+ * ```coffeescript [specscript]
982
+ * close() -> undefined
983
+ * ```
984
+ *
985
+ * Closes the websocket connection to the DevTools server and terminates the Google Chrome for Testing process.
986
+ *
987
+ * Arguments:
988
+ * * (none)
989
+ *
990
+ * Return:
991
+ * * `undefined`
992
+ *
993
+ * ```javascript
994
+ * googleChromeDevTools.close()
995
+ * ```
996
+ */
997
+ close() {
998
+ this.closed = true
999
+ this.websocket.sendClose()
1000
+ this.websocket.on('close', () => {
1001
+ this.googleChromeForTesting.cmd.on('close', () => {
1002
+ this.emit('close')
1003
+ })
1004
+ this.googleChromeForTesting.close()
1005
+ })
1006
+ }
1007
+
1008
+ /**
1009
+ * @name Event: close
1010
+ *
1011
+ * @docs
1012
+ * ```coffeescript [specscript]
1013
+ * emit('close')
1014
+ * ```
1015
+ *
1016
+ * The `close` event. Emitted when the websocket connection to the DevTools server is closed and the Google Chrome for Testing process is terminated.
1017
+ *
1018
+ * Event Data:
1019
+ * * (none)
1020
+ *
1021
+ * ```javascript
1022
+ * googleChromeForTesting.on('close', () => {
1023
+ * console.log('WebSocket connection closed and Google Chrome for Testing process terminated.')
1024
+ * })
1025
+ * ```
1026
+ */
1027
+
937
1028
  }
938
1029
 
1030
+
939
1031
  module.exports = GoogleChromeDevTools
@@ -5,10 +5,11 @@ const path = require('path')
5
5
  const { spawn } = require('child_process')
6
6
  const readline = require('readline')
7
7
  const extract = require('extract-zip')
8
- const HTTP = require('../HTTP')
9
- const XML = require('../XML')
10
- const Readable = require('../Readable')
11
- const walk = require('./walk')
8
+ const HTTP = require('./HTTP')
9
+ const XML = require('./XML')
10
+ const Readable = require('./Readable')
11
+ const walk = require('./internal/walk')
12
+ const sleep = require('./internal/sleep')
12
13
 
13
14
  async function getChromeVersions() {
14
15
  const http = new HTTP()
@@ -17,7 +18,7 @@ async function getChromeVersions() {
17
18
  return data
18
19
  }
19
20
 
20
- function updateConsoleLog(message) {
21
+ function updateConsoleLog(message, platform) {
21
22
  readline.cursorTo(process.stdout, 0, undefined);
22
23
  readline.clearLine(process.stdout, 0);
23
24
  process.stdout.write(message);
@@ -32,7 +33,11 @@ function getPlatform() {
32
33
 
33
34
  if (platform == 'mac') {
34
35
  platform = `${platform}-${arch}`
35
- } else {
36
+ }
37
+ else if (platform == 'win32') {
38
+ platform = `win${arch.slice(1)}`
39
+ }
40
+ else {
36
41
  platform = `${platform}${arch.slice(1)}`
37
42
  }
38
43
 
@@ -57,13 +62,26 @@ async function getChromeUrl() {
57
62
  }
58
63
 
59
64
  async function installChrome() {
65
+ const platform = getPlatform()
66
+ const delimiter = platform.startsWith('win') ? '\\' : '/'
60
67
  const url = await getChromeUrl.call(this)
61
- let filepath = `${this.chromeDir}/${url.replace('https://storage.googleapis.com/chrome-for-testing-public/', '')}`
62
- if (!filepath.startsWith('/')) {
68
+
69
+ let filepath = `${this.chromeDir}${delimiter}${url.replace('https://storage.googleapis.com/chrome-for-testing-public/', '')}`
70
+ if (platform.startsWith('win')) {
71
+ filepath = filepath.replace(/\//g, '\\')
72
+ if (!filepath.startsWith(`${__dirname[0]}:`)) {
73
+ filepath = path.join(process.cwd(), filepath)
74
+ }
75
+ } else if (!filepath.startsWith('/')) {
63
76
  filepath = path.join(process.cwd(), filepath)
64
77
  }
65
- let parentDir = `${filepath.split('/').slice(0, -1).join('/')}`
66
- if (!parentDir.startsWith('/')) {
78
+
79
+ let parentDir = `${filepath.split(delimiter).slice(0, -1).join(delimiter)}`
80
+ if (platform.startsWith('win')) {
81
+ if (!filepath.startsWith(`${__dirname[0]}:`)) {
82
+ parentDir = path.join(process.cwd(), parentDir)
83
+ }
84
+ } else if (!parentDir.startsWith('/')) {
67
85
  parentDir = path.join(process.cwd(), parentDir)
68
86
  }
69
87
  await fs.promises.mkdir(parentDir, { recursive: true })
@@ -80,9 +98,9 @@ async function installChrome() {
80
98
  response.on('data', chunk => {
81
99
  downloadedLength += chunk.length
82
100
  if (downloadedLength == contentLength) {
83
- updateConsoleLog(`Downloading ${url} (${downloadedLength} / ${contentLength} bytes)\n`)
101
+ updateConsoleLog(`Downloading ${url} (${downloadedLength} / ${contentLength} bytes)\n`, platform)
84
102
  } else {
85
- updateConsoleLog(`Downloading ${url} (${downloadedLength} / ${contentLength} bytes)`)
103
+ updateConsoleLog(`Downloading ${url} (${downloadedLength} / ${contentLength} bytes)`, platform)
86
104
  }
87
105
 
88
106
  fileStream.write(chunk)
@@ -97,7 +115,13 @@ async function installChrome() {
97
115
  })
98
116
  await promise
99
117
 
100
- await extract(filepath, { dir: parentDir })
118
+ console.log('Extracting', filepath)
119
+ try {
120
+ await extract(filepath, { dir: parentDir })
121
+ } catch (_error) {
122
+ await sleep(1000)
123
+ await extract(filepath, { dir: parentDir })
124
+ }
101
125
  }
102
126
 
103
127
  async function getChromeFilepath() {
@@ -108,13 +132,15 @@ async function getChromeFilepath() {
108
132
 
109
133
  try {
110
134
  for await (const filepath of walk(parentDir)) {
111
- console.log(filepath)
112
135
  if (platform.startsWith('mac') && filepath.endsWith('Google Chrome for Testing')) {
113
136
  return filepath
114
137
  }
115
138
  if (platform.startsWith('linux') && filepath.endsWith('chrome')) {
116
139
  return filepath
117
140
  }
141
+ if (platform.startsWith('win') && filepath.endsWith('chrome.exe')) {
142
+ return filepath
143
+ }
118
144
  }
119
145
  } catch (error) {
120
146
  if (error.code == 'ENOENT') {
@@ -139,11 +165,48 @@ async function getChromeFilepath() {
139
165
  * headless: boolean,
140
166
  * userDataDir: string,
141
167
  * useMockKeychain: boolean,
142
- * }) -> GoogleChromeForTesting
168
+ * }) -> googleChromeForTesting GoogleChromeForTesting
169
+ * ```
170
+ *
171
+ * Presidium GoogleChromeForTesting client for test automation.
172
+ *
173
+ * Arguments:
174
+ * * `options`
175
+ * * `chromeVersion` - the version of Google Chrome for Testing to download. Defaults to `'stable'`.
176
+ * * `chromeDir` - the directory that Google Chrome for Testing will install to. Defaults to ``google-chrome-for-testing'`.
177
+ * * `remoteDebuggingPort` - the port that the Chrome DevTools Protocol server will listen on. Defaults to `9222`
178
+ * * `headless` - whether to run Google Chrome for Testing in headless mode. Defaults to `false`.
179
+ * * `userDataDir` - directory for storing user profile data such as history, bookmarks, cookies, and settings. Defaults to `tmp/chrome`.
180
+ * * `useMockKeychain` - whether to use a mock keychain instead of the system's real security keychain. Defaults to `true`.
181
+ *
182
+ * Returns:
183
+ * * `googleChromeForTesting` - an instance of the `GoogleChromeForTesting` client.
184
+ *
185
+ * ```javascript
186
+ * const googleChromeForTesting = new GoogleChromeForTesting({ chromeVersion: 'stable' })
187
+ * await googleChromeForTesting.init()
143
188
  * ```
144
189
  *
145
- * References:
190
+ * Google Chrome for Testing versions:
146
191
  * * [Chrome for Testing availability](https://googlechromelabs.github.io/chrome-for-testing/)
192
+ *
193
+ * Supported platforms:
194
+ * * `mac-arm64`
195
+ * * `linux64`
196
+ * * `win64`
197
+ *
198
+ * ## Further Installation
199
+ * Some further installation may be required for Linux platforms.
200
+ *
201
+ * ### Install headless dependencies for Amazon Linux 2023 / Red Hat
202
+ * ```sh
203
+ * sudo dnf install -y cairo pango nss nspr atk at-spi2-atk cups-libs libdrm libxkbcommon libXcomposite libXdamage libXfixes libXrandr mesa-libgbm alsa-lib
204
+ * ```
205
+ *
206
+ * ### Install headless dependencies for Ubuntu / Debian
207
+ * ```sh
208
+ * sudo apt-get update && sudo apt-get install -y libcairo2 libpango-1.0-0 libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libatspi2.0-0 libcups2 libdrm-dev libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm-dev libasound2-dev
209
+ * ```
147
210
  */
148
211
  class GoogleChromeForTesting {
149
212
  constructor(options = {}) {
@@ -151,8 +214,8 @@ class GoogleChromeForTesting {
151
214
  this.chromeDir = options.chromeDir ?? 'google-chrome-for-testing'
152
215
  this.remoteDebuggingPort = options.remoteDebuggingPort ?? 9222
153
216
  this.headless = options.headless ?? false
154
- this.userDataDir = options.userDataDir ?? './tmp/chrome'
155
- this.useMockKeychain = options.useMockKeychain ?? false
217
+ this.userDataDir = options.userDataDir ?? 'tmp/chrome'
218
+ this.useMockKeychain = options.useMockKeychain ?? true
156
219
  this.devtoolsUrl = undefined
157
220
  }
158
221
 
@@ -163,8 +226,26 @@ class GoogleChromeForTesting {
163
226
  * ```coffeescript [specscript]
164
227
  * init() -> Promise<>
165
228
  * ```
229
+ *
230
+ * Initializes the `GoogleChromeForTesting` client.
231
+ *
232
+ * Arguments:
233
+ * * (none)
234
+ *
235
+ * Returns:
236
+ * * `promise` - a promise that resolves when the initialization process is done.
237
+ *
238
+ * ```javascript
239
+ * const googleChromeForTesting = new GoogleChromeForTesting()
240
+ *
241
+ * await googleChromeForTesting.init()
242
+ * ```
166
243
  */
167
244
  async init() {
245
+ if (this.devtoolsUrl) {
246
+ return undefined
247
+ }
248
+
168
249
  const chromeFilepath = await getChromeFilepath.call(this)
169
250
 
170
251
  const cmd = spawn(chromeFilepath, [
@@ -172,22 +253,29 @@ class GoogleChromeForTesting {
172
253
  `--user-data-dir=${this.userDataDir}`,
173
254
  ...this.headless ? ['--headless'] : [],
174
255
  ...this.useMockKeychain ? ['--use-mock-keychain'] : [],
256
+ '--no-sandbox',
175
257
  ])
176
258
  cmd.stdout.pipe(process.stdout)
177
259
  cmd.stderr.pipe(process.stderr)
178
260
 
179
- const devtoolsUrlPromiseWithResolvers = Promise.withResolvers()
261
+ let devtoolsUrlResolve
262
+ const devtoolsUrlPromise = new Promise(_resolve => {
263
+ devtoolsUrlResolve = _resolve
264
+ })
180
265
  cmd.stderr.on('data', chunk => {
181
266
  const line = chunk.toString('utf8').trim()
182
267
  if (line.includes('DevTools listening on')) {
183
268
  const devtoolsUrl = line.replace('DevTools listening on ', '')
184
- devtoolsUrlPromiseWithResolvers.resolve(devtoolsUrl)
269
+ devtoolsUrlResolve(devtoolsUrl)
185
270
  }
186
271
  })
187
272
 
188
- const spawnPromiseWithResolvers = Promise.withResolvers()
273
+ let spawnResolve
274
+ const spawnPromise = new Promise(_resolve => {
275
+ spawnResolve = _resolve
276
+ })
189
277
  cmd.on('spawn', () => {
190
- spawnPromiseWithResolvers.resolve()
278
+ spawnResolve()
191
279
  })
192
280
 
193
281
  cmd.on('error', error => {
@@ -205,8 +293,10 @@ class GoogleChromeForTesting {
205
293
 
206
294
  this.cmd = cmd
207
295
 
208
- await spawnPromiseWithResolvers.promise
209
- this.devtoolsUrl = await devtoolsUrlPromiseWithResolvers.promise
296
+ await spawnPromise
297
+ this.devtoolsUrl = await devtoolsUrlPromise
298
+
299
+ return undefined
210
300
  }
211
301
 
212
302
  /**
@@ -216,6 +306,18 @@ class GoogleChromeForTesting {
216
306
  * ```coffeescript [specscript]
217
307
  * close() -> undefined
218
308
  * ```
309
+ *
310
+ * Terminates the Google Chrome for Testing process.
311
+ *
312
+ * Arguments:
313
+ * * (none)
314
+ *
315
+ * Return:
316
+ * * `undefined`
317
+ *
318
+ * ```javascript
319
+ * googleChromeForTesting.close()
320
+ * ```
219
321
  */
220
322
  close() {
221
323
  this.cmd.kill('SIGKILL')
package/README.md CHANGED
@@ -323,10 +323,14 @@ await docker.createService({
323
323
 
324
324
  ## [Automate tests with Google Chrome for Testing](https://presidium.services/docs/GoogleChromeDevTools)
325
325
  ```javascript
326
+ const GoogleChromeForTesting = require('presidium/GoogleChromeForTesting')
326
327
  const GoogleChromeDevTools = require('presidium/GoogleChromeDevTools')
327
328
 
328
- const googleChromeDevTools = new GoogleChromeDevTools()
329
- await googleChromeDevTools.init() // downloads Google Chrome for Testing
329
+ const googleChromeForTesting = new GoogleChromeForTesting()
330
+ await googleChromeForTesting.init() // downloads Google Chrome for Testing
331
+
332
+ const googleChromeDevTools = new GoogleChromeDevTools(googleChromeForTesting)
333
+ await googleChromeDevTools.init() // connects to the DevTools server
330
334
 
331
335
  // get targets
332
336
  const targetsData = await googleChromeDevTools.Target.getTargets()
package/index.js CHANGED
@@ -6,6 +6,7 @@ module.exports = {
6
6
  DynamoDBTable: require('./DynamoDBTable.js'),
7
7
  ECR: require('./ECR.js'),
8
8
  GoogleChromeDevTools: require('./GoogleChromeDevTools.js'),
9
+ GoogleChromeForTesting: require('./GoogleChromeForTesting.js'),
9
10
  HTTP: require('./HTTP.js'),
10
11
  NpmToken: require('./NpmToken.js'),
11
12
  OptionalValidator: require('./OptionalValidator.js'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "presidium",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "A library for creating web services",
5
5
  "author": "Richard Tong",
6
6
  "license": "MIT",
@@ -19,6 +19,7 @@
19
19
  "DynamoDBTable.js",
20
20
  "ECR.js",
21
21
  "GoogleChromeDevTools.js",
22
+ "GoogleChromeForTesting.js",
22
23
  "HTTP.js",
23
24
  "index.js",
24
25
  "NpmToken.js",
@@ -1,50 +0,0 @@
1
- const Test = require('thunk-test')
2
- const assert = require('assert')
3
- const fs = require('fs')
4
- const { exec } = require('child_process')
5
- const Readable = require('../Readable')
6
- const GoogleChromeForTesting = require('./GoogleChromeForTesting')
7
-
8
- const test = new Test('GoogleChromeForTesting', async function integration() {
9
- await fs.promises.rm('google-chrome-for-testing', { recursive: true, force: true })
10
-
11
- const cmd = await exec('ps aux | grep "Google Chrome for Testing" | awk \'{print $2}\' | xargs kill', {
12
- stdio: 'inherit',
13
- })
14
-
15
- {
16
- const googleChromeForTesting = new GoogleChromeForTesting({
17
- userDataDir: `${__dirname}/tmp/chrome`,
18
- useMockKeychain: true,
19
- })
20
- await googleChromeForTesting.init()
21
-
22
- assert.equal(typeof googleChromeForTesting.devtoolsUrl, 'string')
23
- assert(googleChromeForTesting.devtoolsUrl.startsWith('ws://'))
24
-
25
- googleChromeForTesting.close()
26
- }
27
-
28
- {
29
- const googleChromeForTesting = new GoogleChromeForTesting({
30
- userDataDir: `${__dirname}/tmp/chrome`,
31
- useMockKeychain: true,
32
- })
33
- await googleChromeForTesting.init()
34
-
35
- assert.equal(typeof googleChromeForTesting.devtoolsUrl, 'string')
36
- assert(googleChromeForTesting.devtoolsUrl.startsWith('ws://'))
37
-
38
- googleChromeForTesting.close()
39
- }
40
-
41
- await fs.promises.rm('google-chrome-for-testing', { recursive: true, force: true })
42
-
43
- console.log('Success')
44
- }).case()
45
-
46
- if (process.argv[1] == __filename) {
47
- test()
48
- }
49
-
50
- module.exports = test