• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

uber-web / probe.gl / 3732260095

pending completion
3732260095

push

github

GitHub
chore: stricter typescript settings (#213)

262 of 614 branches covered (42.67%)

Branch coverage included in aggregate %.

29 of 29 new or added lines in 6 files covered. (100.0%)

549 of 969 relevant lines covered (56.66%)

283546.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

3.09
/modules/test-utils/src/browser-automation/browser-driver.ts
1
// probe.gl, MIT license
2

3
import puppeteer, {Browser, Page} from 'puppeteer';
4
import ChildProcess from 'child_process';
5

6
import {COLOR, Log} from '@probe.gl/log';
7
import {getAvailablePort} from '../utils/process-utils';
8

9
type BrowserDriverProps = {
10
  id?: string;
11
};
12

13
const DEFAULT_SERVER_CONFIG = {
1✔
14
  command: 'webpack-dev-server',
15
  arguments: [],
16
  port: 'auto',
17
  wait: 2000,
18
  options: {maxBuffer: 5000 * 1024}
19
};
20

21
// https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#puppeteerlaunchoptions
22
const DEFAULT_PUPPETEER_OPTIONS = {
1✔
23
  headless: false,
24
  defaultViewport: {width: 800, height: 600}
25
};
26

27
const AUTO_PORT_START = 5000;
1✔
28

29
function noop() {} // eslint-disable-line @typescript-eslint/no-empty-function
30

31
export default class BrowserDriver {
32
  readonly id: string;
33
  logger: Log;
34
  server: ChildProcess.ChildProcessWithoutNullStreams = null;
×
35
  port: number | 'auto' = null;
×
36
  browser: Browser = null;
×
37
  page: Page = null;
×
38

39
  constructor(options?: BrowserDriverProps) {
40
    const {id = 'browser-driver'} = options || {};
×
41
    this.id = id;
×
42
    this.logger = new Log({id});
×
43
  }
44

45
  async startBrowser(options?: {
46
    headless?: boolean;
47
    defaultViewport?: {width: number; height: number};
48
  }): Promise<void> {
49
    options = Object.assign({}, DEFAULT_PUPPETEER_OPTIONS, options);
×
50
    if (this.browser) {
×
51
      return;
×
52
    }
53
    this.browser = await puppeteer.launch(options);
×
54
    console.log(await this.browser.version()); // eslint-disable-line
×
55
  }
56

57
  async openPage(options?: {
58
    url?: string;
59
    exposeFunctions?: object;
60
    onLoad?: (...args: any) => any;
61
    onConsole?: (...args: any) => any;
62
    onError?: (...args: any) => any;
63
  }): Promise<void> {
64
    const {
65
      url = 'http://localhost',
×
66
      exposeFunctions = {},
×
67
      onLoad = noop,
×
68
      onConsole = noop,
×
69
      onError = noop
×
70
    } = options || {};
×
71

72
    if (!this.browser) {
×
73
      throw new Error('No browser instance is found. Forgot to call startBrowser()?');
×
74
    }
75

76
    this.page = await this.browser.newPage();
×
77

78
    // https://ourcodeworld.com/articles/read/1106/how-to-solve-puppeteer-timeouterror-navigation-timeout-of-30000-ms-exceeded
79
    this.page.setDefaultNavigationTimeout(0);
×
80

81
    // attach events
82
    this.page.on('load', onLoad);
×
83
    this.page.on('console', onConsole);
×
84
    this.page.on('error', onError);
×
85

86
    const promises: Promise<any>[] = [];
×
87
    for (const name in exposeFunctions) {
×
88
      promises.push(this.page.exposeFunction(name, exposeFunctions[name]));
×
89
    }
90
    await Promise.all(promises);
×
91

92
    await this.page.goto(url);
×
93
  }
94

95
  async stopBrowser(): Promise<void> {
96
    if (this.browser) {
×
97
      await this.browser.close();
×
98
      this.browser = null;
×
99
    }
100
  }
101

102
  /** Starts a web server with the provided configs.
103
   * Resolves to the bound url if successful
104
   */
105
  async startServer(config?: {
106
    port?: number | 'auto';
107
    command?: string;
108
    options?: object;
109
  }): Promise<string> {
110
    // @ts-expect-error
111
    config = normalizeServerConfig(config, this.logger);
×
112

113
    const port = config.port === 'auto' ? await getAvailablePort(AUTO_PORT_START) : config.port;
×
114

115
    // @ts-expect-error
116
    const args = [...config.arguments];
×
117
    if (port) {
×
118
      args.push('--port', port);
×
119
    }
120

121
    const server = ChildProcess.spawn(config.command, args, config.options);
×
122
    this.server = server;
×
123
    this.port = port;
×
124

125
    return await new Promise((resolve, reject) => {
×
126
      server.stderr.on('data', onError);
×
127
      server.on('error', onError);
×
128
      server.on('close', () => () => {
×
129
        this.server = null;
×
130
      });
131

132
      const successTimer = setTimeout(() => {
×
133
        const url = `http://localhost:${this.port}`;
×
134

135
        this.logger.log({
×
136
          message: `Started ${config.command} at ${url}`,
137
          color: COLOR.BRIGHT_GREEN
138
        })();
139

140
        resolve(url);
×
141
        // @ts-expect-error
142
      }, config.wait);
143

144
      function onError(error) {
145
        clearTimeout(successTimer);
×
146
        reject(error);
×
147
      }
148
    });
149
  }
150

151
  async stopServer(): Promise<void> {
152
    if (this.server) {
×
153
      this.server.kill();
×
154
      this.server = null;
×
155
    }
156
  }
157

158
  /* eslint-disable no-process-exit */
159
  async exit(statusCode: number = 0): Promise<void> {
×
160
    try {
×
161
      await this.stopBrowser();
×
162
      await this.stopServer();
×
163
      process.exit(statusCode);
×
164
    } catch (error) {
165
      this.logger.error(error instanceof Error ? error.message : String(error));
×
166
      process.exit(1);
×
167
    }
168
  }
169
}
170

171
function normalizeServerConfig(config, logger) {
172
  const result = Object.assign({}, DEFAULT_SERVER_CONFIG);
×
173

174
  // Handle legacy configs
175
  if (config.process) {
×
176
    result.command = config.process;
×
177
    logger.deprecated('process', 'command');
×
178
  }
179
  if (config.parameters) {
×
180
    result.arguments = config.parameters;
×
181
    logger.deprecated('parameters', 'arguments');
×
182
  }
183

184
  Object.assign(result, config, {
×
185
    options: Object.assign({}, result.options, config.options)
186
  });
187

188
  return result;
×
189
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc