Repository: elastic/synthetics
Pull request #380 (2021-09-20 12:05:39)

Append Elastic/synthetics to UA string
Fixes https://github.com/elastic/synthetics/issues/232

Appends Elastic/Synthetics to UA string. 

This PR uses a pretty ugly way to determin current user agent string and append the desired string into it.

Not sure if there is a way better way to do this. Struggling with playwright docs on how to get the current user agent string without opening a browser context. 



apmmachine: 
## :broken_heart: Tests Failed


<!-- BUILD BADGES-->
> _the below badges are clickable and redirect to their specific view in the CI or DOCS_
[![Pipeline View](https://img.shields.io/badge/pipeline-pipeline%20-green)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-rum%2Felastic-synthetics%2FPR-380/detail/PR-380/1//pipeline) [![Test View](https://img.shields.io/badge/test-test-green)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-rum%2Felastic-synthetics%2FPR-380/detail/PR-380/1//tests) [![Changes](https://img.shields.io/badge/changes-changes-green)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-rum%2Felastic-synthetics%2FPR-380/detail/PR-380/1//changes) [![Artifacts](https://img.shields.io/badge/artifacts-artifacts-yellow)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-rum%2Felastic-synthetics%2FPR-380/detail/PR-380/1//artifacts) [![preview](https://img.shields.io/badge/docs-preview-yellowgreen)](http://synthetics_380.docs-preview.app.elstc.co/diff)  [![preview](https://img.shields.io/badge/elastic-observability-blue)](https://ci-stats.elastic.co/app/apm/services/apm-ci/transactions/view?rangeFrom=2021-09-20T11:55:52.521Z&rangeTo=2021-09-20T12:15:52.521Z&transactionName=apm-agent-rum/elastic-synthetics/PR-{number}&transactionType=unknown&latencyAggregationType=avg&traceId=495f442901bce6fb81952ba8edb55602&transactionId=483f446c2aabe484)

<!-- BUILD SUMMARY-->
<details><summary>Expand to view the summary</summary>
<p>

#### Build stats




* Start Time: 2021-09-20T12:05:52.521+0000


* Duration: 7 min 29 sec


* Commit: f30f62205c30ccda4b038e40fd023f32fc4ac16a



#### Test stats :test_tube:

| Test         | Results                         |
| ------------ | :-----------------------------: |
| Failed       | 2  |
| Passed       | 127  |
| Skipped      | 0 |
| Total        | 129   |


#### Trends :test_tube:


![Image of Build Times](https://apm-ci.elastic.co/job/apm-agent-rum/job/elastic-synthetics/job/PR-380/buildTimeGraph/png)


![Image of Tests](https://apm-ci.elastic.co/job/apm-agent-rum/job/elastic-synthetics/job/PR-380/1/testReport/history/countGraph/png?start=0&amp;end=25)


</p>
</details>

<!-- TEST RESULTS IF ANY-->

  ### Test errors [![2](https://img.shields.io/badge/2%20-red)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-rum%2Felastic-synthetics%2FPR-380/detail/PR-380/1//tests)
<details><summary>Expand to view the tests failures</summary><p>
    
    
##### `Test / CLI screenshots with device emulation – CLI screenshots with device emulation`


<ul>
<li>no error details</li>
<details><summary>Expand to view the stacktrace</summary><p>

```
 Error: Failed: "Error SyntaxError: Unexpected end of JSON input could not parse data '{\"type\":\"screenshot/block\",\"_id\":\"71bb77ed1acf85f13f1e8a2ab3cf3401850f7b9d\",\"@timestamp\":1632139976353934.8,\"root_fields\":{\"os\":{\"platform\":\"linux\"},\"package\":{\"name\":\"@elastic/synthetics\",\"version\":\"1.0.0-beta.14\"}},\"blob\":\"/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAFyALQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'"
    at Env.it (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:95:24)
    at it (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-jasmine2/build/jasmine/jasmineLight.js:100:21)
    at Suite.<anonymous> (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/__tests__/cli.test.ts:163:3)
    at addSpecsToSuite (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-jasmine2/build/jasmine/Env.js:444:51)
    at Env.describe (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-jasmine2/build/jasmine/Env.js:414:11)
    at describe (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-jasmine2/build/jasmine/jasmineLight.js:88:18)
    at Object.<anonymous> (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/__tests__/cli.test.ts:30:1)
    at Runtime._execModule (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-runtime/build/index.js:1299:24)
    at Runtime._loadModule (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-runtime/build/index.js:898:12)
    at Runtime.requireModule (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/node_modules/jest-runtime/build/index.js:746:10) 
 ```
</p></details>
</ul>
    
##### `Test / Gatherer append Elastic/Synthetics as part of userAgent – Gatherer append Elastic/Synthetics as part of userAgent`


<ul>
<li>no error details</li>
<details><summary>Expand to view the stacktrace</summary><p>

```
 Error: expect(received).toBe(expected) // Object.is equality

Expected: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics"
Received: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics"
    at Object.<anonymous> (/var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380/src/github.com/elastic/synthetics/__tests__/core/gatherer.test.ts:61:67) 
 ```
</p></details>
</ul>
    
</p></details>


<!-- STEPS ERRORS IF ANY -->









### Steps errors [![1](https://img.shields.io/badge/1%20-red)](https://apm-ci.elastic.co/blue/organizations/jenkins/apm-agent-rum%2Felastic-synthetics%2FPR-380/detail/PR-380/1//pipeline)
  <details><summary>Expand to view the steps failures</summary>
  <p>


  
  
  
  
##### `Runs the tests`
<ul>
<li>Took 1 min 57 sec . View more details <a href="https://apm-ci.elastic.co//blue/rest/organizations/jenkins/pipelines/apm-agent-rum/pipelines/elastic-synthetics/pipelines/PR-380/runs/1/steps/300/log/?start=0">here</a></li>
<li>Description: <code>./node_modules/.bin/jest --ci --reporters=default --reporters=jest-junit</code></l1>
</ul>
  
  </p>
  </details>



<!-- LOG OUTPUT-->
### Log output

<details><summary>Expand to view the last 100 lines of log output</summary>
<p>

```
[2021-09-20T12:12:59.244Z] PASS __tests__/index.test.ts
[2021-09-20T12:12:59.508Z] FAIL __tests__/core/gatherer.test.ts
[2021-09-20T12:12:59.508Z]   ● Gatherer › append Elastic/Synthetics as part of userAgent
[2021-09-20T12:12:59.508Z] 
[2021-09-20T12:12:59.508Z]     expect(received).toBe(expected) // Object.is equality
[2021-09-20T12:12:59.508Z] 
[2021-09-20T12:12:59.509Z]     Expected: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics"
[2021-09-20T12:12:59.509Z]     Received: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics"
[2021-09-20T12:12:59.509Z] 
[2021-09-20T12:12:59.509Z]       59 |     const driver = await Gatherer.setupDriver({ wsEndpoint });
[2021-09-20T12:12:59.509Z]       60 | 
[2021-09-20T12:12:59.509Z]     > 61 |     expect(await driver.page.evaluate(() => navigator.userAgent)).toBe(
[2021-09-20T12:12:59.509Z]          |                                                                   ^
[2021-09-20T12:12:59.509Z]       62 |       'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics'
[2021-09-20T12:12:59.509Z]       63 |     );
[2021-09-20T12:12:59.509Z]       64 | 
[2021-09-20T12:12:59.509Z] 
[2021-09-20T12:12:59.509Z]       at Object.<anonymous> (__tests__/core/gatherer.test.ts:61:67)
[2021-09-20T12:12:59.509Z] 
[2021-09-20T12:13:00.459Z] PASS __tests__/plugins/plugin-manager.test.ts
[2021-09-20T12:13:01.855Z] PASS __tests__/plugins/performance.test.ts
[2021-09-20T12:13:01.855Z] PASS __tests__/config.test.ts
[2021-09-20T12:13:03.254Z] PASS __tests__/sdk/trace-processor.test.ts
[2021-09-20T12:13:04.642Z] PASS __tests__/plugins/tracing.test.ts
[2021-09-20T12:13:05.584Z] PASS __tests__/core/browser-service.test.ts
[2021-09-20T12:13:05.584Z] PASS __tests__/dsl/journey.test.ts
[2021-09-20T12:13:06.156Z] PASS __tests__/core/logger.test.ts
[2021-09-20T12:13:18.407Z] FAIL __tests__/cli.test.ts (51.036 s)
[2021-09-20T12:13:18.407Z]   ● CLI › screenshots with device emulation
[2021-09-20T12:13:18.407Z] 
[2021-09-20T12:13:18.407Z]     Failed: "Error SyntaxError: Unexpected end of JSON input could not parse data '{\"type\":\"screenshot/block\",\"_id\":\"71bb77ed1acf85f13f1e8a2ab3cf3401850f7b9d\",\"@timestamp\":1632139976353934.8,\"root_fields\":{\"os\":{\"platform\":\"linux\"},\"package\":{\"name\":\"@elastic/synthetics\",\"version\":\"1.0.0-beta.14\"}},\"blob\":\"/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAFyALQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'"
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]       161 |   });
[2021-09-20T12:13:18.408Z]       162 | 
[2021-09-20T12:13:18.408Z]     > 163 |   it('screenshots with device emulation', async () => {
[2021-09-20T12:13:18.408Z]           |   ^
[2021-09-20T12:13:18.408Z]       164 |     const cli = new CLIMock()
[2021-09-20T12:13:18.408Z]       165 |       .args([
[2021-09-20T12:13:18.408Z]       166 |         join(FIXTURES_DIR, 'fake.journey.ts'),
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]       at Env.it (node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:95:24)
[2021-09-20T12:13:18.408Z]       at Suite.<anonymous> (__tests__/cli.test.ts:163:3)
[2021-09-20T12:13:18.408Z]       at Object.<anonymous> (__tests__/cli.test.ts:30:1)
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z] A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks.
[2021-09-20T12:13:18.408Z] Summary of all failing tests
[2021-09-20T12:13:18.408Z] FAIL __tests__/core/gatherer.test.ts
[2021-09-20T12:13:18.408Z]   ● Gatherer › append Elastic/Synthetics as part of userAgent
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]     expect(received).toBe(expected) // Object.is equality
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]     Expected: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics"
[2021-09-20T12:13:18.408Z]     Received: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics"
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]       59 |     const driver = await Gatherer.setupDriver({ wsEndpoint });
[2021-09-20T12:13:18.408Z]       60 | 
[2021-09-20T12:13:18.408Z]     > 61 |     expect(await driver.page.evaluate(() => navigator.userAgent)).toBe(
[2021-09-20T12:13:18.408Z]          |                                                                   ^
[2021-09-20T12:13:18.408Z]       62 |       'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/94.0.4595.0 Safari/537.36 Elastic/Synthetics'
[2021-09-20T12:13:18.408Z]       63 |     );
[2021-09-20T12:13:18.408Z]       64 | 
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]       at Object.<anonymous> (__tests__/core/gatherer.test.ts:61:67)
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z] FAIL __tests__/cli.test.ts (51.036 s)
[2021-09-20T12:13:18.408Z]   ● CLI › screenshots with device emulation
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]     Failed: "Error SyntaxError: Unexpected end of JSON input could not parse data '{\"type\":\"screenshot/block\",\"_id\":\"71bb77ed1acf85f13f1e8a2ab3cf3401850f7b9d\",\"@timestamp\":1632139976353934.8,\"root_fields\":{\"os\":{\"platform\":\"linux\"},\"package\":{\"name\":\"@elastic/synthetics\",\"version\":\"1.0.0-beta.14\"}},\"blob\":\"/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAFyALQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'"
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]       161 |   });
[2021-09-20T12:13:18.408Z]       162 | 
[2021-09-20T12:13:18.408Z]     > 163 |   it('screenshots with device emulation', async () => {
[2021-09-20T12:13:18.408Z]           |   ^
[2021-09-20T12:13:18.408Z]       164 |     const cli = new CLIMock()
[2021-09-20T12:13:18.408Z]       165 |       .args([
[2021-09-20T12:13:18.408Z]       166 |         join(FIXTURES_DIR, 'fake.journey.ts'),
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z]       at Env.it (node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:95:24)
[2021-09-20T12:13:18.408Z]       at Suite.<anonymous> (__tests__/cli.test.ts:163:3)
[2021-09-20T12:13:18.408Z]       at Object.<anonymous> (__tests__/cli.test.ts:30:1)
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z] 
[2021-09-20T12:13:18.408Z] Test Suites: 2 failed, 19 passed, 21 total
[2021-09-20T12:13:18.408Z] Tests:       2 failed, 113 passed, 115 total
[2021-09-20T12:13:18.408Z] Snapshots:   13 passed, 13 total
[2021-09-20T12:13:18.408Z] Time:        52.021 s
[2021-09-20T12:13:18.408Z] Ran all test suites.
[2021-09-20T12:13:19.637Z] Post stage
[2021-09-20T12:13:19.666Z] Recording test results
[2021-09-20T12:13:19.761Z] [Checks API] No suitable checks publisher found.
[2021-09-20T12:13:19.901Z] Stage "E2e Test" skipped due to earlier failure(s)
[2021-09-20T12:13:19.984Z] Stage "Release" skipped due to earlier failure(s)
[2021-09-20T12:13:20.025Z] Stage "Release" skipped due to earlier failure(s)
[2021-09-20T12:13:20.112Z] Stage "Release" skipped due to earlier failure(s)
[2021-09-20T12:13:20.627Z] Running on Jenkins in /var/lib/jenkins/workspace/nt-rum_elastic-synthetics_PR-380
[2021-09-20T12:13:20.718Z] [INFO] getVaultSecret: Getting secrets
[2021-09-20T12:13:20.771Z] Masking supported pattern matches of $VAULT_ADDR or $VAULT_ROLE_ID or $VAULT_SECRET_ID
[2021-09-20T12:13:21.788Z] + chmod 755 generate-build-data.sh
[2021-09-20T12:13:21.788Z] + ./generate-build-data.sh https://apm-ci.elastic.co/blue/rest/organizations/jenkins/pipelines/apm-agent-rum/elastic-synthetics/PR-380/ https://apm-ci.elastic.co/blue/rest/organizations/jenkins/pipelines/apm-agent-rum/elastic-synthetics/PR-380/runs/1 FAILURE 448988
[2021-09-20T12:13:21.788Z] INFO: curl https://apm-ci.elastic.co/blue/rest/organizations/jenkins/pipelines/apm-agent-rum/elastic-synthetics/PR-380/runs/1/steps/?limit=10000 -o steps-info.json

```

</p>
</details>



<!--COMMENT_GENERATED_WITH_ID_comment.id-->
vigneshshanmugam: We discussed offline and decided to go with using browser based session approach to get the user agent. 

Benchmarks if anyone is curious
```
Session: 3.505ms
Context: 245.072ms
```

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b5265c..eda684f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
 
 All notable changes to this project will be documented in this file.
 
+## v1.0.0-beta.14 (2021-09-16)
+
+### Bug fixes
+
+- Bring back `--suite-params` flag support to support Heartbeat 7.15 [#379](https://github.com/elastic/synthetics/pull/379)
+
 ## v1.0.0-beta.13 (2021-09-16)
 
 ### Features
diff --git a/src/core/gatherer.ts b/src/core/gatherer.ts
index 7764840..732d605 100644
--- a/src/core/gatherer.ts
+++ b/src/core/gatherer.ts
@@ -46,12 +46,21 @@ export class Gatherer {
         Gatherer.browser = await chromium.launch(playwrightOptions);
       }
     }
-    const context = await Gatherer.browser.newContext(playwrightOptions);
+    const context = await Gatherer.browser.newContext({
+      ...playwrightOptions,
+      userAgent: await Gatherer.getUserAgent(),
+    });
     const page = await context.newPage();
     const client = await context.newCDPSession(page);
     return { browser: Gatherer.browser, context, page, client };
   }
 
+  static async getUserAgent() {
+    const session = await Gatherer.browser.newBrowserCDPSession();
+    const { userAgent } = await session.send('Browser.getVersion');
+    return userAgent + ' Elastic/Synthetics';
+  }
+
   /**
    * Starts recording all events related to the v8 devtools protocol
    * https://chromedevtools.github.io/devtools-protocol/v8/


Test patch:
diff --git a/__tests__/core/gatherer.test.ts b/__tests__/core/gatherer.test.ts
index c078a9d..6b3ac70 100644
--- a/__tests__/core/gatherer.test.ts
+++ b/__tests__/core/gatherer.test.ts
@@ -26,10 +26,21 @@
 import { Gatherer } from '../../src/core/gatherer';
 import { PluginManager } from '../../src/plugins';
 import { wsEndpoint } from '../utils/test-config';
+import { devices } from 'playwright-chromium';
+import { Server } from '../utils/server';
 
 jest.mock('../../src/plugins/network');
 
 describe('Gatherer', () => {
+  let server: Server;
+  beforeAll(async () => {
+    server = await Server.create();
+  });
+
+  afterAll(async () => {
+    await server.close();
+  });
+
   it('boot and close browser', async () => {
     const driver = await Gatherer.setupDriver({ wsEndpoint });
     expect(typeof driver.page.goto).toBe('function');
@@ -54,4 +65,55 @@ describe('Gatherer', () => {
     expect(network.start).toHaveBeenCalled();
     await Gatherer.stop();
   });
+
+  describe('Elastic UA identifier', () => {
+    it('works on a single page', async () => {
+      const driver = await Gatherer.setupDriver({ wsEndpoint });
+      expect(await driver.page.evaluate(() => navigator.userAgent)).toContain(
+        ' Elastic/Synthetics'
+      );
+      await Gatherer.dispose(driver);
+      await Gatherer.stop();
+    });
+
+    it('works with device emulation', async () => {
+      const driver = await Gatherer.setupDriver({
+        wsEndpoint,
+        playwrightOptions: { ...devices['Galaxy S9+'] },
+      });
+      expect(await driver.page.evaluate(() => navigator.userAgent)).toContain(
+        ' Elastic/Synthetics'
+      );
+      await Gatherer.dispose(driver);
+      await Gatherer.stop();
+    });
+
+    it('works with popup window', async () => {
+      const driver = await Gatherer.setupDriver({
+        wsEndpoint,
+        playwrightOptions: { ...devices['Galaxy S9+'] },
+      });
+      const { page, context } = driver;
+      await page.goto(server.TEST_PAGE);
+      context.on('request', request => {
+        expect(request.headers()['user-agent']).toContain(
+          ' Elastic/Synthetics'
+        );
+      });
+      await page.setContent(
+        '<a target=_blank rel=noopener href="/popup.html">popup</a>'
+      );
+      const [page1] = await Promise.all([
+        context.waitForEvent('page'),
+        page.click('a'),
+      ]);
+      await page1.waitForLoadState();
+      expect(await page1.evaluate(() => navigator.userAgent)).toContain(
+        ' Elastic/Synthetics'
+      );
+      expect(await page1.textContent('body')).toEqual('Not found');
+      await Gatherer.dispose(driver);
+      await Gatherer.stop();
+    });
+  });
 });
diff --git a/__tests__/core/runner.test.ts b/__tests__/core/runner.test.ts
index 96d4fb2..146880e 100644
--- a/__tests__/core/runner.test.ts
+++ b/__tests__/core/runner.test.ts
@@ -314,7 +314,7 @@ describe('runner', () => {
     });
   });
 
-  it('run api - match journey name explict', async () => {
+  it('run api - match journey name explicit', async () => {
     runner.addJourney(new Journey({ name: 'j1' }, noop));
     runner.addJourney(new Journey({ name: 'j2' }, noop));
     expect(