Skip to content

Commit fc9c841

Browse files
committed
get version numbers for Chrome on MacOS
1 parent 8cd668f commit fc9c841

File tree

7 files changed

+335
-0
lines changed

7 files changed

+335
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
jest.mock('../src/sh')
19+
import { getBrowserInfo, ChromeChannel } from '../src/chrome'
20+
import { sh } from '../src/sh'
21+
22+
const mockSh = (sh as unknown) as jest.Mock<any, any>
23+
24+
describe('chrome browser info', () => {
25+
it('should get stable chrome browser info', async () => {
26+
mockSh.mockReturnValueOnce(
27+
Promise.resolve({ stdout: 'Google Chrome 76.0.3809.132\n', stderr: '' })
28+
)
29+
expect(await getBrowserInfo(ChromeChannel.stable)).toEqual({
30+
binary: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
31+
version: '76.0.3809.132',
32+
channel: 'stable',
33+
})
34+
})
35+
it('should get all chrome info', async () => {
36+
mockSh.mockImplementation(binary => {
37+
if (binary.includes('Canary')) {
38+
return Promise.resolve({
39+
stdout: 'Google Chrome 79.0.3915.0 canary\n',
40+
stderr: '',
41+
})
42+
} else if (binary.includes('Beta')) {
43+
return Promise.resolve({
44+
stdout: 'Google Chrome 75.0.3770.75 beta\n',
45+
stderr: '',
46+
})
47+
} else {
48+
return Promise.resolve({
49+
stdout: 'Google Chrome 76.0.3809.132\n',
50+
stderr: '',
51+
})
52+
}
53+
})
54+
expect(await getBrowserInfo()).toEqual([
55+
{
56+
binary: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
57+
version: '76.0.3809.132',
58+
channel: 'stable',
59+
},
60+
{
61+
binary:
62+
'/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome',
63+
version: '75.0.3770.75',
64+
channel: 'beta',
65+
},
66+
{
67+
binary:
68+
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
69+
version: '79.0.3915.0',
70+
channel: 'canary',
71+
},
72+
])
73+
mockSh.mockReset()
74+
})
75+
})
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
import { sh } from '../src/sh'
19+
20+
describe('sh', () => {
21+
it('should run a process', async () => {
22+
try {
23+
await sh('node', ['-e', '""'])
24+
} catch {
25+
// should not reach this place
26+
expect(true).toBeFalsy()
27+
}
28+
})
29+
it('should return stdout and stderr', async () => {
30+
const { stdout, stderr } = await sh('node', [
31+
'-e',
32+
'console.log(`out`);console.error(`err`);',
33+
])
34+
expect(stdout).toBe('out\n')
35+
expect(stderr).toBe('err\n')
36+
})
37+
it('should return stdout and stderr even if the process crashes', async () => {
38+
expect.assertions(3)
39+
try {
40+
await sh('node', [
41+
'-e',
42+
'console.log(`out`);console.error(`err`);process.exit(1);',
43+
])
44+
} catch (err) {
45+
expect(err.stdout).toBe('out\n')
46+
expect(err.stderr).toBe('err\n')
47+
expect(err.code).toBe(1)
48+
}
49+
})
50+
})

packages/browser-info/package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "@seleniumhq/browser-info",
3+
"version": "3.5.10",
4+
"private": true,
5+
"description": "Get information about installed browsers",
6+
"author": "Tomer <[email protected]>",
7+
"homepage": "http://github.com/SeleniumHQ/selenium-ide",
8+
"license": "Apache-2.0",
9+
"scripts": {
10+
"build": "tsc",
11+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
12+
"prepublishOnly": "yarn clean && yarn build",
13+
"watch": "yarn build -w"
14+
},
15+
"main": "dist/index.js",
16+
"types": "dist/index.d.ts",
17+
"files": [
18+
"dist"
19+
],
20+
"repository": {
21+
"type": "git",
22+
"url": "git+https://github.com/SeleniumHQ/selenium-ide.git"
23+
},
24+
"bugs": {
25+
"url": "https://github.com/SeleniumHQ/selenium-ide/issues"
26+
}
27+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
export const sh = jest.fn()
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
import { sh } from './sh'
19+
20+
const CHROME_STABLE_MACOS_INSTALL_LOCATIONS = [
21+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
22+
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
23+
]
24+
25+
const CHROME_BETA_MACOS_INSTALL_LOCATIONS = [
26+
'/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome',
27+
'~/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome',
28+
]
29+
30+
const CHROME_CANARY_MACOS_INSTALL_LOCATIONS = [
31+
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
32+
'~/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
33+
]
34+
35+
export async function getBrowserInfo(channel?: ChromeChannel) {
36+
if (channel) {
37+
switch (channel) {
38+
case ChromeChannel.stable: {
39+
return await getChromeInfo(CHROME_STABLE_MACOS_INSTALL_LOCATIONS)
40+
}
41+
case ChromeChannel.beta: {
42+
return await getChromeInfo(CHROME_BETA_MACOS_INSTALL_LOCATIONS)
43+
}
44+
case ChromeChannel.canary: {
45+
return await getChromeInfo(CHROME_CANARY_MACOS_INSTALL_LOCATIONS)
46+
}
47+
}
48+
}
49+
return (await Promise.all([
50+
getChromeInfo(CHROME_STABLE_MACOS_INSTALL_LOCATIONS),
51+
getChromeInfo(CHROME_BETA_MACOS_INSTALL_LOCATIONS),
52+
getChromeInfo(CHROME_CANARY_MACOS_INSTALL_LOCATIONS),
53+
])).filter(Boolean)
54+
}
55+
56+
async function getChromeInfo(installLocations: string[]): Promise<BrowserInfo> {
57+
for await (let binary of installLocations) {
58+
const { stdout } = await sh(binary, ['--version'])
59+
return {
60+
binary,
61+
...parseChromeEdition(stdout),
62+
}
63+
}
64+
throw new Error('Unable to find Chrome installation')
65+
}
66+
67+
function parseChromeEdition(output: string) {
68+
const [version, channel] = output
69+
.split('\n')[0]
70+
.replace('Google Chrome ', '')
71+
.split(' ')
72+
return {
73+
version,
74+
channel: channel ? (channel as ChromeChannel) : ChromeChannel.stable,
75+
}
76+
}
77+
78+
export interface BrowserInfo {
79+
channel: ChromeChannel
80+
binary: string
81+
version: string
82+
}
83+
84+
export enum ChromeChannel {
85+
stable = 'stable',
86+
beta = 'beta',
87+
canary = 'canary',
88+
}

packages/browser-info/src/sh.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
import { spawn } from 'child_process'
19+
20+
export async function sh(
21+
command: string,
22+
args: string[] = [],
23+
options?: object
24+
): Promise<Output> {
25+
let resolve: (value: Output) => void, reject: (reason: Error & Output) => void
26+
const p = new Promise<Output>((res, rej) => {
27+
resolve = res
28+
reject = rej
29+
})
30+
31+
let stdout = '',
32+
stderr = ''
33+
const cp = spawn(command, args, options)
34+
35+
cp.stdout.on('data', data => {
36+
stdout += data.toString()
37+
})
38+
39+
cp.stderr.on('data', data => {
40+
stderr += data.toString()
41+
})
42+
43+
cp.on('close', (code: number) => {
44+
if (!code) {
45+
resolve({ stdout, stderr })
46+
} else {
47+
const err = new Error(`Process exited with code ${code}`) as Error &
48+
Output &
49+
ErrorWithExitCode
50+
err.stdout = stdout
51+
err.stderr = stderr
52+
err.code = code
53+
reject(err)
54+
}
55+
})
56+
57+
return await p
58+
}
59+
60+
interface Output {
61+
stdout: string
62+
stderr: string
63+
}
64+
65+
interface ErrorWithExitCode {
66+
code: number
67+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"rootDir": "src",
5+
"outDir": "dist"
6+
},
7+
"include": [
8+
"src"
9+
]
10+
}

0 commit comments

Comments
 (0)