/
next-webdriver.js
219 lines (186 loc) · 5.66 KB
/
next-webdriver.js
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/// <reference types="./next-webdriver" />
import os from 'os'
import path from 'path'
import fetch from 'node-fetch'
import Chain from './wd-chain'
import { Builder, By } from 'selenium-webdriver'
import { Options as ChromeOptions } from 'selenium-webdriver/chrome'
import { Options as SafariOptions } from 'selenium-webdriver/safari'
import { Options as FireFoxOptions } from 'selenium-webdriver/firefox'
const {
BROWSER_NAME: browserName = 'chrome',
BROWSERSTACK,
BROWSERSTACK_USERNAME,
BROWSERSTACK_ACCESS_KEY,
HEADLESS,
CHROME_BIN,
LEGACY_SAFARI,
} = process.env
let capabilities = {}
const isChrome = browserName === 'chrome'
const isSafari = browserName === 'safari'
const isFirefox = browserName === 'firefox'
const isIE = browserName === 'internet explorer'
if (process.env.ChromeWebDriver) {
process.env.PATH = `${process.env.ChromeWebDriver}${path.delimiter}${process.env.PATH}`
}
const isBrowserStack =
BROWSERSTACK && BROWSERSTACK_USERNAME && BROWSERSTACK_ACCESS_KEY
if (isBrowserStack) {
const safariOpts = {
os: 'OS X',
os_version: 'Mojave',
browser: 'Safari',
}
const safariLegacyOpts = {
os: 'OS X',
os_version: 'Sierra',
browserName: 'Safari',
browser_version: '10.1',
}
const ieOpts = {
os: 'Windows',
os_version: '10',
browser: 'IE',
}
const firefoxOpts = {
os: 'Windows',
os_version: '10',
browser: 'Firefox',
}
const sharedOpts = {
'browserstack.local': true,
'browserstack.video': false,
'browserstack.user': BROWSERSTACK_USERNAME,
'browserstack.key': BROWSERSTACK_ACCESS_KEY,
'browserstack.localIdentifier': global.browserStackLocalId,
}
capabilities = {
...capabilities,
...sharedOpts,
...(isIE ? ieOpts : {}),
...(isSafari ? (LEGACY_SAFARI ? safariLegacyOpts : safariOpts) : {}),
...(isFirefox ? firefoxOpts : {}),
}
}
let chromeOptions = new ChromeOptions()
let firefoxOptions = new FireFoxOptions()
let safariOptions = new SafariOptions()
if (HEADLESS) {
const screenSize = { width: 1280, height: 720 }
chromeOptions = chromeOptions.headless().windowSize(screenSize)
firefoxOptions = firefoxOptions.headless().windowSize(screenSize)
}
if (CHROME_BIN) {
chromeOptions = chromeOptions.setChromeBinaryPath(path.resolve(CHROME_BIN))
}
let seleniumServer
if (isBrowserStack) {
seleniumServer = 'http://hub-cloud.browserstack.com/wd/hub'
} else if (global.seleniumServerPort) {
seleniumServer = `http://localhost:${global.seleniumServerPort}/wd/hub`
}
let browser = new Builder()
.usingServer(seleniumServer)
.withCapabilities(capabilities)
.forBrowser(browserName)
.setChromeOptions(chromeOptions)
.setFirefoxOptions(firefoxOptions)
.setSafariOptions(safariOptions)
.build()
global.wd = browser
/*
# Methods to match
- elementByCss
- elementsByCss
- waitForElementByCss
- elementByCss.text
- elementByCss.click
*/
let initialWindow
let deviceIP = 'localhost'
const getDeviceIP = async () => {
const networkIntfs = os.networkInterfaces()
// find deviceIP to use with BrowserStack
for (const intf of Object.keys(networkIntfs)) {
const addresses = networkIntfs[intf]
for (const { internal, address, family } of addresses) {
if (family !== 'IPv4' || internal) continue
try {
const res = await fetch(`http://${address}:${global._newTabPort}`)
if (res.ok) {
deviceIP = address
break
}
} catch (_) {}
}
}
}
// eslint-disable-next-line no-unused-vars
const freshWindow = async () => {
// First we close all extra windows left over
let allWindows = await browser.getAllWindowHandles()
for (const win of allWindows) {
if (win === initialWindow) continue
try {
await browser.switchTo().window(win)
await browser.close()
} catch (_) {}
}
await browser.switchTo().window(initialWindow)
// now we open a fresh window
await browser.get(`http://${deviceIP}:${global._newTabPort}`)
const newTabLink = await browser.findElement(By.css('#new'))
await newTabLink.click()
allWindows = await browser.getAllWindowHandles()
const newWindow = allWindows.find(win => win !== initialWindow)
await browser.switchTo().window(newWindow)
}
export default async (appPort, path, waitHydration = true) => {
if (!initialWindow) {
initialWindow = await browser.getWindowHandle()
}
if (isBrowserStack && deviceIP === 'localhost') {
await getDeviceIP()
}
// browser.switchTo().window() fails with `missing field `handle``
// in safari and firefox so disabling freshWindow since our
// tests shouldn't rely on it
if (isChrome) {
await freshWindow()
}
const url = `http://${deviceIP}:${appPort}${path}`
console.log(`\n> Loading browser with ${url}\n`)
await browser.get(url)
console.log(`\n> Loaded browser with ${url}\n`)
// Wait for application to hydrate
if (waitHydration) {
console.log(`\n> Waiting hydration for ${url}\n`)
await browser.executeAsyncScript(function() {
var callback = arguments[arguments.length - 1]
// if it's not a Next.js app return
if (document.documentElement.innerHTML.indexOf('__NEXT_DATA__') === -1) {
callback()
}
if (window.__NEXT_HYDRATED) {
callback()
} else {
var timeout = setTimeout(callback, 10 * 1000)
window.__NEXT_HYDRATED_CB = function() {
clearTimeout(timeout)
callback()
}
}
})
console.log(`\n> Hydration complete for ${url}\n`)
}
const promiseProp = new Set(['then', 'catch', 'finally'])
return new Proxy(new Chain(browser), {
get(obj, prop) {
if (obj[prop] || promiseProp.has(prop)) {
return obj[prop]
}
return browser[prop]
},
})
}