feat(sdk-trace-web): web worker support (#2719)
Co-authored-by: Valentin Marchaud <contact@vmarchaud.fr>
This commit is contained in:
parent
e32879abbc
commit
04f9edd12f
|
@ -81,6 +81,41 @@ jobs:
|
||||||
run: npm run test:browser
|
run: npm run test:browser
|
||||||
- name: Report Coverage
|
- name: Report Coverage
|
||||||
run: npm run codecov:browser
|
run: npm run codecov:browser
|
||||||
|
webworker-tests-stable:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: circleci/node:14-browsers
|
||||||
|
env:
|
||||||
|
NPM_CONFIG_UNSAFE_PERM: true
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
- name: Permission Setup
|
||||||
|
run: sudo chmod -R 777 /github /__w
|
||||||
|
|
||||||
|
- name: restore lerna
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
node_modules
|
||||||
|
*/*/node_modules
|
||||||
|
key: browser-tests-stable-${{ runner.os }}-${{ matrix.node_version }}-${{ hashFiles('**/package.json') }}
|
||||||
|
|
||||||
|
- name: Bootstrap
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
npm install --ignore-scripts
|
||||||
|
npx lerna bootstrap --no-ci --hoist --nohoist='zone.js'
|
||||||
|
|
||||||
|
- name: Build 🔧
|
||||||
|
run: |
|
||||||
|
npm run compile
|
||||||
|
|
||||||
|
- name: Unit tests
|
||||||
|
run: npm run test:webworker
|
||||||
|
- name: Report Coverage
|
||||||
|
run: npm run codecov:webworker
|
||||||
node-tests-experimental:
|
node-tests-experimental:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
@ -164,3 +199,42 @@ jobs:
|
||||||
- name: Report Coverage
|
- name: Report Coverage
|
||||||
working-directory: experimental
|
working-directory: experimental
|
||||||
run: npm run codecov:browser
|
run: npm run codecov:browser
|
||||||
|
webworker-tests-experimental:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: circleci/node:14-browsers
|
||||||
|
env:
|
||||||
|
NPM_CONFIG_UNSAFE_PERM: true
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
- name: Permission Setup
|
||||||
|
run: sudo chmod -R 777 /github /__w
|
||||||
|
|
||||||
|
- name: restore lerna
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
experimental/node_modules
|
||||||
|
experimental/*/*/node_modules
|
||||||
|
key: browser-tests-experimental-${{ runner.os }}-${{ matrix.node_version }}-${{ hashFiles('**/package.json') }}
|
||||||
|
|
||||||
|
- name: Bootstrap
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
working-directory: experimental
|
||||||
|
run: |
|
||||||
|
npm install --ignore-scripts
|
||||||
|
npx lerna bootstrap --no-ci --hoist --nohoist='zone.js'
|
||||||
|
|
||||||
|
- name: Build 🔧
|
||||||
|
working-directory: experimental
|
||||||
|
run: |
|
||||||
|
npm run compile
|
||||||
|
|
||||||
|
- name: Unit tests
|
||||||
|
working-directory: experimental
|
||||||
|
run: npm run test:webworker
|
||||||
|
- name: Report Coverage
|
||||||
|
working-directory: experimental
|
||||||
|
run: npm run codecov:webworker
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Web API Usages Guidance
|
||||||
|
|
||||||
|
The packages of OpenTelemetry that targeting web platforms should be compatible
|
||||||
|
with the following web environments:
|
||||||
|
|
||||||
|
- [Browsing Context](https://developer.mozilla.org/en-US/docs/Glossary/Browsing_context),
|
||||||
|
- [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers).
|
||||||
|
|
||||||
|
As such, the usage of Web API that depends on APIs like [`window`],
|
||||||
|
[`document`] and [`navigator`] is discouraged.
|
||||||
|
|
||||||
|
If the use of the browsing context API is necessary, like adding `onload`
|
||||||
|
listeners, an alternative code path for Web Worker environment should also be
|
||||||
|
supported.
|
||||||
|
|
||||||
|
It is an exception to above guidance if the package is instrumenting the
|
||||||
|
browsing context only.
|
||||||
|
|
||||||
|
[`window`]: https://developer.mozilla.org/en-US/docs/Web/API/window
|
||||||
|
[`document`]: https://developer.mozilla.org/en-US/docs/Web/API/Document
|
||||||
|
[`navigator]: https://developer.mozilla.org/en-US/docs/Web/API/Navigator
|
|
@ -1,9 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
"env": {
|
||||||
"mocha": true,
|
"mocha": true,
|
||||||
"commonjs": true,
|
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"jquery": true
|
|
||||||
},
|
},
|
||||||
...require('../../../eslint.config.js')
|
...require('../../../eslint.config.js')
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
reporters: ['spec', 'coverage-istanbul'],
|
reporters: ['spec', 'coverage-istanbul'],
|
||||||
files: ['test/index-webpack.ts'],
|
files: ['test/index-webpack.ts'],
|
||||||
preprocessors: { 'test/index-webpack.ts': ['webpack'] },
|
preprocessors: {
|
||||||
|
'test/index-webpack*.ts': ['webpack']
|
||||||
|
},
|
||||||
webpackMiddleware: { noInfo: true }
|
webpackMiddleware: { noInfo: true }
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*!
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const baseConfig = require('./karma.base');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...baseConfig,
|
||||||
|
frameworks: ['mocha-webworker'],
|
||||||
|
|
||||||
|
files: [{
|
||||||
|
pattern: 'test/index-webpack.worker.ts',
|
||||||
|
included: false,
|
||||||
|
}],
|
||||||
|
};
|
|
@ -16,11 +16,13 @@
|
||||||
"version:update": "lerna run version:update",
|
"version:update": "lerna run version:update",
|
||||||
"test": "lerna run test",
|
"test": "lerna run test",
|
||||||
"test:browser": "lerna run test:browser",
|
"test:browser": "lerna run test:browser",
|
||||||
|
"test:webworker": "lerna run test:webworker",
|
||||||
"test:backcompat": "lerna run test:backcompat",
|
"test:backcompat": "lerna run test:backcompat",
|
||||||
"bootstrap": "lerna bootstrap --hoist --nohoist='zone.js'",
|
"bootstrap": "lerna bootstrap --hoist --nohoist='zone.js'",
|
||||||
"changelog": "lerna-changelog",
|
"changelog": "lerna-changelog",
|
||||||
"codecov": "lerna run codecov",
|
"codecov": "lerna run codecov",
|
||||||
"codecov:browser": "lerna run codecov:browser",
|
"codecov:browser": "lerna run codecov:browser",
|
||||||
|
"codecov:webworker": "lerna run codecov:webworker",
|
||||||
"predocs-test": "npm run docs",
|
"predocs-test": "npm run docs",
|
||||||
"docs": "typedoc && touch docs/.nojekyll",
|
"docs": "typedoc && touch docs/.nojekyll",
|
||||||
"docs-deploy": "gh-pages --dotfiles --dist docs",
|
"docs-deploy": "gh-pages --dotfiles --dist docs",
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
"env": {
|
||||||
"mocha": true,
|
"mocha": true,
|
||||||
"commonjs": true,
|
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"jquery": true
|
|
||||||
},
|
},
|
||||||
...require('../../eslint.config.js')
|
...require('../../eslint.config.js')
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*!
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const karmaWebpackConfig = require('../../karma.webpack');
|
||||||
|
const karmaBaseConfig = require('../../karma.worker');
|
||||||
|
|
||||||
|
module.exports = (config) => {
|
||||||
|
config.set(Object.assign({}, karmaBaseConfig, {
|
||||||
|
webpack: karmaWebpackConfig
|
||||||
|
}))
|
||||||
|
};
|
|
@ -13,9 +13,11 @@
|
||||||
"lint": "eslint . --ext .ts",
|
"lint": "eslint . --ext .ts",
|
||||||
"lint:fix": "eslint . --ext .ts --fix",
|
"lint:fix": "eslint . --ext .ts --fix",
|
||||||
"codecov:browser": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
|
"codecov:browser": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
|
||||||
|
"codecov:webworker": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
|
||||||
"version": "node ../../scripts/version-update.js",
|
"version": "node ../../scripts/version-update.js",
|
||||||
"tdd": "karma start",
|
"tdd": "karma start",
|
||||||
"test:browser": "nyc karma start --single-run",
|
"test:browser": "nyc karma start --single-run",
|
||||||
|
"test:webworker": "nyc karma start karma.worker.js --single-run",
|
||||||
"watch": "tsc --build --watch tsconfig.all.json",
|
"watch": "tsc --build --watch tsconfig.all.json",
|
||||||
"precompile": "lerna run version --scope $(npm pkg get name) --include-dependencies",
|
"precompile": "lerna run version --scope $(npm pkg get name) --include-dependencies",
|
||||||
"prewatch": "npm run precompile"
|
"prewatch": "npm run precompile"
|
||||||
|
@ -69,6 +71,7 @@
|
||||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||||
"karma-jquery": "0.2.4",
|
"karma-jquery": "0.2.4",
|
||||||
"karma-mocha": "2.0.1",
|
"karma-mocha": "2.0.1",
|
||||||
|
"karma-mocha-webworker": "1.3.0",
|
||||||
"karma-spec-reporter": "0.0.32",
|
"karma-spec-reporter": "0.0.32",
|
||||||
"karma-webpack": "4.0.2",
|
"karma-webpack": "4.0.2",
|
||||||
"mocha": "7.2.0",
|
"mocha": "7.2.0",
|
||||||
|
|
|
@ -164,7 +164,7 @@ export function getResource(
|
||||||
}
|
}
|
||||||
const sorted = sortResources(filteredResources);
|
const sorted = sortResources(filteredResources);
|
||||||
|
|
||||||
if (parsedSpanUrl.origin !== window.location.origin && sorted.length > 1) {
|
if (parsedSpanUrl.origin !== location.origin && sorted.length > 1) {
|
||||||
let corsPreFlightRequest: PerformanceResourceTiming | undefined = sorted[0];
|
let corsPreFlightRequest: PerformanceResourceTiming | undefined = sorted[0];
|
||||||
let mainRequest: PerformanceResourceTiming = findMainRequest(
|
let mainRequest: PerformanceResourceTiming = findMainRequest(
|
||||||
sorted,
|
sorted,
|
||||||
|
@ -421,7 +421,7 @@ export function shouldPropagateTraceHeaders(
|
||||||
}
|
}
|
||||||
const parsedSpanUrl = parseUrl(spanUrl);
|
const parsedSpanUrl = parseUrl(spanUrl);
|
||||||
|
|
||||||
if (parsedSpanUrl.origin === window.location.origin) {
|
if (parsedSpanUrl.origin === location.origin) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return propagateTraceHeaderUrls.some(propagateTraceHeaderUrl =>
|
return propagateTraceHeaderUrls.some(propagateTraceHeaderUrl =>
|
||||||
|
|
|
@ -111,7 +111,7 @@ describe('StackContextManager', () => {
|
||||||
assert.strictEqual(contextManager.active(), ctx1);
|
assert.strictEqual(contextManager.active(), ctx1);
|
||||||
return done();
|
return done();
|
||||||
});
|
});
|
||||||
assert.strictEqual(contextManager.active(), window);
|
assert.strictEqual(contextManager.active(), globalThis);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should finally restore an old context when context is an object', done => {
|
it('should finally restore an old context when context is an object', done => {
|
||||||
|
@ -130,7 +130,7 @@ describe('StackContextManager', () => {
|
||||||
assert.strictEqual(contextManager.active(), ctx1);
|
assert.strictEqual(contextManager.active(), ctx1);
|
||||||
return done();
|
return done();
|
||||||
});
|
});
|
||||||
assert.strictEqual(contextManager.active(), window);
|
assert.strictEqual(contextManager.active(), globalThis);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should forward this, arguments and return value', () => {
|
it('should forward this, arguments and return value', () => {
|
||||||
|
|
|
@ -13,8 +13,13 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
const testsContext = require.context('.', true, /test$/);
|
|
||||||
testsContext.keys().forEach(testsContext);
|
|
||||||
|
|
||||||
const srcContext = require.context('.', true, /src$/);
|
{
|
||||||
srcContext.keys().forEach(srcContext);
|
const testsContext = require.context('./', false, /test$/);
|
||||||
|
testsContext.keys().forEach(testsContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const testsContext = require.context('./window', false, /test$/);
|
||||||
|
testsContext.keys().forEach(testsContext);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const testsContext = require.context('./', false, /test$/);
|
||||||
|
testsContext.keys().forEach(testsContext);
|
|
@ -27,7 +27,6 @@ import * as sinon from 'sinon';
|
||||||
import {
|
import {
|
||||||
addSpanNetworkEvent,
|
addSpanNetworkEvent,
|
||||||
addSpanNetworkEvents,
|
addSpanNetworkEvents,
|
||||||
getElementXPath,
|
|
||||||
getResource,
|
getResource,
|
||||||
normalizeUrl,
|
normalizeUrl,
|
||||||
parseUrl,
|
parseUrl,
|
||||||
|
@ -48,45 +47,6 @@ function createHrTime(startTime: HrTime, addToStart: number): HrTime {
|
||||||
}
|
}
|
||||||
return [seconds, nanos];
|
return [seconds, nanos];
|
||||||
}
|
}
|
||||||
const fixture = `
|
|
||||||
<div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div>
|
|
||||||
<div></div>
|
|
||||||
<div>
|
|
||||||
</div>
|
|
||||||
<div id="text">lorep ipsum</div>
|
|
||||||
<div></div>
|
|
||||||
<div class="btn2">
|
|
||||||
foo
|
|
||||||
<button></button>
|
|
||||||
<button></button>
|
|
||||||
<button id="btn22"></button>
|
|
||||||
<button></button>
|
|
||||||
bar
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
aaaaaaaaa
|
|
||||||
<![CDATA[ /*Some code with < & and what not */ ]]>
|
|
||||||
<button id="btn23"></button>
|
|
||||||
bbb
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
<div id="comment"></div>
|
|
||||||
<div></div>
|
|
||||||
<div id="cdata">
|
|
||||||
<![CDATA[ /*Some code with < & and what not */ ]]>
|
|
||||||
<![CDATA[ /*Some code with < & and what not */ ]]>
|
|
||||||
<![CDATA[ /*Some code with < & and what not */ ]]>
|
|
||||||
bar
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
function createResource(
|
function createResource(
|
||||||
resource = {},
|
resource = {},
|
||||||
|
@ -475,92 +435,11 @@ describe('utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('getElementXPath', () => {
|
|
||||||
let $fixture: any;
|
|
||||||
let child: any;
|
|
||||||
before(() => {
|
|
||||||
$fixture = $(fixture);
|
|
||||||
const body = document.querySelector('body');
|
|
||||||
if (body) {
|
|
||||||
body.appendChild($fixture[0]);
|
|
||||||
child = body.lastChild;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
after(() => {
|
|
||||||
child.parentNode.removeChild(child);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return correct path for element with id and optimise = true', () => {
|
|
||||||
const element = getElementXPath($fixture.find('#btn22')[0], true);
|
|
||||||
assert.strictEqual(element, '//*[@id="btn22"]');
|
|
||||||
assert.strictEqual(
|
|
||||||
$fixture.find('#btn22')[0],
|
|
||||||
getElementByXpath(element)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(
|
|
||||||
'should return correct path for element with id and surrounded by the' +
|
|
||||||
' same type',
|
|
||||||
() => {
|
|
||||||
const element = getElementXPath($fixture.find('#btn22')[0]);
|
|
||||||
assert.strictEqual(element, '//html/body/div/div[4]/div[5]/button[3]');
|
|
||||||
assert.strictEqual(
|
|
||||||
$fixture.find('#btn22')[0],
|
|
||||||
getElementByXpath(element)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
|
||||||
'should return correct path for element with id and and surrounded by' +
|
|
||||||
' text nodes mixed with cnode',
|
|
||||||
() => {
|
|
||||||
const element = getElementXPath($fixture.find('#btn23')[0]);
|
|
||||||
assert.strictEqual(element, '//html/body/div/div[4]/div[6]/button');
|
|
||||||
assert.strictEqual(
|
|
||||||
$fixture.find('#btn23')[0],
|
|
||||||
getElementByXpath(element)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
|
||||||
'should return correct path for text node element surrounded by cdata' +
|
|
||||||
' nodes',
|
|
||||||
() => {
|
|
||||||
const text = $fixture.find('#cdata')[0];
|
|
||||||
const textNode = document.createTextNode('foobar');
|
|
||||||
text.appendChild(textNode);
|
|
||||||
const element = getElementXPath(textNode);
|
|
||||||
assert.strictEqual(element, '//html/body/div/div[4]/div[10]/text()[5]');
|
|
||||||
assert.strictEqual(textNode, getElementByXpath(element));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should return correct path when element is text node', () => {
|
|
||||||
const text = $fixture.find('#text')[0];
|
|
||||||
const textNode = document.createTextNode('foobar');
|
|
||||||
text.appendChild(textNode);
|
|
||||||
const element = getElementXPath(textNode);
|
|
||||||
assert.strictEqual(element, '//html/body/div/div[4]/div[3]/text()[2]');
|
|
||||||
assert.strictEqual(textNode, getElementByXpath(element));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return correct path when element is comment node', () => {
|
|
||||||
const comment = $fixture.find('#comment')[0];
|
|
||||||
const node = document.createComment('foobar');
|
|
||||||
comment.appendChild(node);
|
|
||||||
const element = getElementXPath(node);
|
|
||||||
assert.strictEqual(element, '//html/body/div/div[4]/div[8]/comment()');
|
|
||||||
assert.strictEqual(node, getElementByXpath(element));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('shouldPropagateTraceHeaders', () => {
|
describe('shouldPropagateTraceHeaders', () => {
|
||||||
it('should propagate trace when url is the same as origin', () => {
|
it('should propagate trace when url is the same as origin', () => {
|
||||||
const result = shouldPropagateTraceHeaders(
|
const result = shouldPropagateTraceHeaders(
|
||||||
`${window.location.origin}/foo/bar`
|
`${globalThis.location.origin}/foo/bar`
|
||||||
);
|
);
|
||||||
assert.strictEqual(result, true);
|
assert.strictEqual(result, true);
|
||||||
});
|
});
|
||||||
|
@ -611,14 +490,6 @@ describe('utils', () => {
|
||||||
assert.strictEqual(typeof url[field], 'string');
|
assert.strictEqual(typeof url[field], 'string');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse url with fallback', () => {
|
|
||||||
sinon.stub(window, 'URL').value(undefined);
|
|
||||||
const url = parseUrl('https://opentelemetry.io/foo');
|
|
||||||
urlFields.forEach(field => {
|
|
||||||
assert.strictEqual(typeof url[field], 'string');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('normalizeUrl', () => {
|
describe('normalizeUrl', () => {
|
||||||
|
@ -626,21 +497,5 @@ describe('utils', () => {
|
||||||
const url = normalizeUrl('https://opentelemetry.io/你好');
|
const url = normalizeUrl('https://opentelemetry.io/你好');
|
||||||
assert.strictEqual(url, 'https://opentelemetry.io/%E4%BD%A0%E5%A5%BD');
|
assert.strictEqual(url, 'https://opentelemetry.io/%E4%BD%A0%E5%A5%BD');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse url with fallback', () => {
|
|
||||||
sinon.stub(window, 'URL').value(undefined);
|
|
||||||
const url = normalizeUrl('https://opentelemetry.io/你好');
|
|
||||||
assert.strictEqual(url, 'https://opentelemetry.io/%E4%BD%A0%E5%A5%BD');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function getElementByXpath(path: string) {
|
|
||||||
return document.evaluate(
|
|
||||||
path,
|
|
||||||
document,
|
|
||||||
null,
|
|
||||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
|
||||||
null
|
|
||||||
).singleNodeValue;
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import * as sinon from 'sinon';
|
||||||
|
import {
|
||||||
|
getElementXPath,
|
||||||
|
normalizeUrl,
|
||||||
|
parseUrl,
|
||||||
|
URLLike,
|
||||||
|
} from '../../src/utils';
|
||||||
|
|
||||||
|
const fixture = `
|
||||||
|
<div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
<div id="text">lorep ipsum</div>
|
||||||
|
<div></div>
|
||||||
|
<div class="btn2">
|
||||||
|
foo
|
||||||
|
<button></button>
|
||||||
|
<button></button>
|
||||||
|
<button id="btn22"></button>
|
||||||
|
<button></button>
|
||||||
|
bar
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
aaaaaaaaa
|
||||||
|
<![CDATA[ /*Some code with < & and what not */ ]]>
|
||||||
|
<button id="btn23"></button>
|
||||||
|
bbb
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div id="comment"></div>
|
||||||
|
<div></div>
|
||||||
|
<div id="cdata">
|
||||||
|
<![CDATA[ /*Some code with < & and what not */ ]]>
|
||||||
|
<![CDATA[ /*Some code with < & and what not */ ]]>
|
||||||
|
<![CDATA[ /*Some code with < & and what not */ ]]>
|
||||||
|
bar
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
describe('utils', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
sinon.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getElementXPath', () => {
|
||||||
|
let $fixture: any;
|
||||||
|
let child: any;
|
||||||
|
before(() => {
|
||||||
|
$fixture = $(fixture);
|
||||||
|
const body = document.querySelector('body');
|
||||||
|
if (body) {
|
||||||
|
body.appendChild($fixture[0]);
|
||||||
|
child = body.lastChild;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
after(() => {
|
||||||
|
child.parentNode.removeChild(child);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct path for element with id and optimise = true', () => {
|
||||||
|
const element = getElementXPath($fixture.find('#btn22')[0], true);
|
||||||
|
assert.strictEqual(element, '//*[@id="btn22"]');
|
||||||
|
assert.strictEqual(
|
||||||
|
$fixture.find('#btn22')[0],
|
||||||
|
getElementByXpath(element)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(
|
||||||
|
'should return correct path for element with id and surrounded by the' +
|
||||||
|
' same type',
|
||||||
|
() => {
|
||||||
|
const element = getElementXPath($fixture.find('#btn22')[0]);
|
||||||
|
assert.strictEqual(element, '//html/body/div/div[4]/div[5]/button[3]');
|
||||||
|
assert.strictEqual(
|
||||||
|
$fixture.find('#btn22')[0],
|
||||||
|
getElementByXpath(element)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
'should return correct path for element with id and and surrounded by' +
|
||||||
|
' text nodes mixed with cnode',
|
||||||
|
() => {
|
||||||
|
const element = getElementXPath($fixture.find('#btn23')[0]);
|
||||||
|
assert.strictEqual(element, '//html/body/div/div[4]/div[6]/button');
|
||||||
|
assert.strictEqual(
|
||||||
|
$fixture.find('#btn23')[0],
|
||||||
|
getElementByXpath(element)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
'should return correct path for text node element surrounded by cdata' +
|
||||||
|
' nodes',
|
||||||
|
() => {
|
||||||
|
const text = $fixture.find('#cdata')[0];
|
||||||
|
const textNode = document.createTextNode('foobar');
|
||||||
|
text.appendChild(textNode);
|
||||||
|
const element = getElementXPath(textNode);
|
||||||
|
assert.strictEqual(element, '//html/body/div/div[4]/div[10]/text()[5]');
|
||||||
|
assert.strictEqual(textNode, getElementByXpath(element));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should return correct path when element is text node', () => {
|
||||||
|
const text = $fixture.find('#text')[0];
|
||||||
|
const textNode = document.createTextNode('foobar');
|
||||||
|
text.appendChild(textNode);
|
||||||
|
const element = getElementXPath(textNode);
|
||||||
|
assert.strictEqual(element, '//html/body/div/div[4]/div[3]/text()[2]');
|
||||||
|
assert.strictEqual(textNode, getElementByXpath(element));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct path when element is comment node', () => {
|
||||||
|
const comment = $fixture.find('#comment')[0];
|
||||||
|
const node = document.createComment('foobar');
|
||||||
|
comment.appendChild(node);
|
||||||
|
const element = getElementXPath(node);
|
||||||
|
assert.strictEqual(element, '//html/body/div/div[4]/div[8]/comment()');
|
||||||
|
assert.strictEqual(node, getElementByXpath(element));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseUrl', () => {
|
||||||
|
const urlFields: Array<keyof URLLike> = [
|
||||||
|
'hash',
|
||||||
|
'host',
|
||||||
|
'hostname',
|
||||||
|
'href',
|
||||||
|
'origin',
|
||||||
|
'password',
|
||||||
|
'pathname',
|
||||||
|
'port',
|
||||||
|
'protocol',
|
||||||
|
'search',
|
||||||
|
'username',
|
||||||
|
];
|
||||||
|
it('should parse url with fallback', () => {
|
||||||
|
sinon.stub(globalThis, 'URL').value(undefined);
|
||||||
|
const url = parseUrl('https://opentelemetry.io/foo');
|
||||||
|
urlFields.forEach(field => {
|
||||||
|
assert.strictEqual(typeof url[field], 'string');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalizeUrl', () => {
|
||||||
|
it('should parse url with fallback', () => {
|
||||||
|
sinon.stub(globalThis, 'URL').value(undefined);
|
||||||
|
const url = normalizeUrl('https://opentelemetry.io/你好');
|
||||||
|
assert.strictEqual(url, 'https://opentelemetry.io/%E4%BD%A0%E5%A5%BD');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getElementByXpath(path: string) {
|
||||||
|
return document.evaluate(
|
||||||
|
path,
|
||||||
|
document,
|
||||||
|
null,
|
||||||
|
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||||
|
null
|
||||||
|
).singleNodeValue;
|
||||||
|
}
|
Loading…
Reference in New Issue