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
|
||||
- name: Report Coverage
|
||||
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:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -164,3 +199,42 @@ jobs:
|
|||
- name: Report Coverage
|
||||
working-directory: experimental
|
||||
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 = {
|
||||
"env": {
|
||||
"mocha": true,
|
||||
"commonjs": true,
|
||||
"browser": true,
|
||||
"jquery": true
|
||||
},
|
||||
...require('../../../eslint.config.js')
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ module.exports = {
|
|||
},
|
||||
reporters: ['spec', 'coverage-istanbul'],
|
||||
files: ['test/index-webpack.ts'],
|
||||
preprocessors: { 'test/index-webpack.ts': ['webpack'] },
|
||||
preprocessors: {
|
||||
'test/index-webpack*.ts': ['webpack']
|
||||
},
|
||||
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",
|
||||
"test": "lerna run test",
|
||||
"test:browser": "lerna run test:browser",
|
||||
"test:webworker": "lerna run test:webworker",
|
||||
"test:backcompat": "lerna run test:backcompat",
|
||||
"bootstrap": "lerna bootstrap --hoist --nohoist='zone.js'",
|
||||
"changelog": "lerna-changelog",
|
||||
"codecov": "lerna run codecov",
|
||||
"codecov:browser": "lerna run codecov:browser",
|
||||
"codecov:webworker": "lerna run codecov:webworker",
|
||||
"predocs-test": "npm run docs",
|
||||
"docs": "typedoc && touch docs/.nojekyll",
|
||||
"docs-deploy": "gh-pages --dotfiles --dist docs",
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module.exports = {
|
||||
"env": {
|
||||
"mocha": true,
|
||||
"commonjs": true,
|
||||
"browser": true,
|
||||
"jquery": true
|
||||
},
|
||||
...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:fix": "eslint . --ext .ts --fix",
|
||||
"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",
|
||||
"tdd": "karma start",
|
||||
"test:browser": "nyc karma start --single-run",
|
||||
"test:webworker": "nyc karma start karma.worker.js --single-run",
|
||||
"watch": "tsc --build --watch tsconfig.all.json",
|
||||
"precompile": "lerna run version --scope $(npm pkg get name) --include-dependencies",
|
||||
"prewatch": "npm run precompile"
|
||||
|
@ -69,6 +71,7 @@
|
|||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-jquery": "0.2.4",
|
||||
"karma-mocha": "2.0.1",
|
||||
"karma-mocha-webworker": "1.3.0",
|
||||
"karma-spec-reporter": "0.0.32",
|
||||
"karma-webpack": "4.0.2",
|
||||
"mocha": "7.2.0",
|
||||
|
|
|
@ -164,7 +164,7 @@ export function getResource(
|
|||
}
|
||||
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 mainRequest: PerformanceResourceTiming = findMainRequest(
|
||||
sorted,
|
||||
|
@ -421,7 +421,7 @@ export function shouldPropagateTraceHeaders(
|
|||
}
|
||||
const parsedSpanUrl = parseUrl(spanUrl);
|
||||
|
||||
if (parsedSpanUrl.origin === window.location.origin) {
|
||||
if (parsedSpanUrl.origin === location.origin) {
|
||||
return true;
|
||||
} else {
|
||||
return propagateTraceHeaderUrls.some(propagateTraceHeaderUrl =>
|
||||
|
|
|
@ -111,7 +111,7 @@ describe('StackContextManager', () => {
|
|||
assert.strictEqual(contextManager.active(), ctx1);
|
||||
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 => {
|
||||
|
@ -130,7 +130,7 @@ describe('StackContextManager', () => {
|
|||
assert.strictEqual(contextManager.active(), ctx1);
|
||||
return done();
|
||||
});
|
||||
assert.strictEqual(contextManager.active(), window);
|
||||
assert.strictEqual(contextManager.active(), globalThis);
|
||||
});
|
||||
|
||||
it('should forward this, arguments and return value', () => {
|
||||
|
|
|
@ -13,8 +13,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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 {
|
||||
addSpanNetworkEvent,
|
||||
addSpanNetworkEvents,
|
||||
getElementXPath,
|
||||
getResource,
|
||||
normalizeUrl,
|
||||
parseUrl,
|
||||
|
@ -48,45 +47,6 @@ function createHrTime(startTime: HrTime, addToStart: number): HrTime {
|
|||
}
|
||||
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(
|
||||
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', () => {
|
||||
it('should propagate trace when url is the same as origin', () => {
|
||||
const result = shouldPropagateTraceHeaders(
|
||||
`${window.location.origin}/foo/bar`
|
||||
`${globalThis.location.origin}/foo/bar`
|
||||
);
|
||||
assert.strictEqual(result, true);
|
||||
});
|
||||
|
@ -611,14 +490,6 @@ describe('utils', () => {
|
|||
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', () => {
|
||||
|
@ -626,21 +497,5 @@ describe('utils', () => {
|
|||
const url = normalizeUrl('https://opentelemetry.io/你好');
|
||||
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