Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@netflix/x-test

Package Overview
Dependencies
Maintainers
12
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@netflix/x-test - npm Package Compare versions

Comparing version 1.0.0-rc.15 to 1.0.0-rc.16

test/test-reporter.html

7

demo/index.js

@@ -13,6 +13,3 @@ import { assert, it, test, todo, cover } from '../x-test.js';

todo(
`is
handled...`,
`multi
todo(`multi
line

@@ -25,3 +22,3 @@ test`,

todo('foo', 'demonstrate passing "todo"', () => {
todo('demonstrate passing "todo"', () => {
assert(1);

@@ -28,0 +25,0 @@ });

import { it, skip, test, waitFor } from '../x-test.js';
skip('takes too long', 'loooong test', async () => {
skip('loooong test', async () => {
await new Promise(resolve => setTimeout(resolve, 1000));

@@ -5,0 +5,0 @@ throw new Error(`i'm broken.`);

@@ -7,4 +7,4 @@ import { assert, it, skip } from '../x-test.js';

skip('false is still not true', 'do the impossible', () => {
skip('do the impossible', () => {
assert(false);
});

@@ -13,4 +13,4 @@ import { assert, it, todo } from '../x-test.js';

todo('make false true', 'do the impossible', () => {
todo('do the impossible', () => {
assert(false);
});
{
"name": "@netflix/x-test",
"version": "1.0.0-rc.15",
"version": "1.0.0-rc.16",
"description": "a simple, tap-compliant test runner for the browser",

@@ -5,0 +5,0 @@ "main": "x-test.js",

@@ -14,5 +14,3 @@ import { assert, it, test, todo, cover } from '../x-test.js';

todo(
`is
handled...`,
`multi
`multi
line

@@ -25,3 +23,3 @@ test`,

todo('foo', 'demonstrate passing "todo"', () => {
todo('demonstrate passing "todo"', () => {
assert(1);

@@ -33,3 +31,4 @@ });

test('./nested/');
test('./test-reporter.html');
test('./test-coverage.html');
test('./test-tap.html');
import { it, skip, test, waitFor } from '../x-test.js';
skip('takes too long', 'loooong test', async () => {
skip('loooong test', async () => {
await new Promise(resolve => setTimeout(resolve, 1000));

@@ -5,0 +5,0 @@ throw new Error(`i'm broken.`);

@@ -7,4 +7,4 @@ import { assert, it, skip } from '../x-test.js';

skip('false is still not true', 'do the impossible', () => {
skip('do the impossible', () => {
assert(false);
});

@@ -13,4 +13,4 @@ import { assert, it, todo } from '../x-test.js';

todo('make false true', 'do the impossible', () => {
todo('do the impossible', () => {
assert(false);
});

@@ -14,3 +14,3 @@ import { it, assert, __Tap__ } from '../x-test.js';

assert(__Tap__.testLine(false, 1, 'first test') === 'not ok - 1 first test');
assert(__Tap__.testLine(false, 1, 'first test', 'todo', 'because') === 'not ok - 1 first test # todo because');
assert(__Tap__.testLine(false, 1, 'first test', 'TODO') === 'not ok - 1 first test # TODO');
});

@@ -17,0 +17,0 @@

@@ -1,41 +0,56 @@

/**
* x-test - a simple, tap-compliant test runner for the browser.
*/
const TAG_LINE = 'x-test - a simple, tap-compliant test runner for the browser.';
// Export interface.
export { test, it, skip, todo, waitFor, assert, cover };
export const assert = (assertion, message) => {
Test.assert(assertion, message);
};
// Export "private" testing handles.
export const test = href => {
Test.test(_test, href);
};
export const it = (description, callback, interval) => {
Test.it(_test, null, description, callback, interval);
};
export const skip = (description, callback, interval) => {
Test.it(_test, 'SKIP', description, callback, interval);
};
export const todo = (description, callback, interval) => {
Test.it(_test, 'TODO', description, callback, interval);
};
export const waitFor = promise => {
Test.waitFor(_test, promise);
};
export const cover = (relativePath, goal) => {
Test.cover(_test, relativePath, goal);
};
export { XTestReporter as __XTestReporter__, Tap as __Tap__, Test as __Test__, RootTest as __RootTest__ };
class XTestReporter extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
set outputs(value) {
this[Symbol.for('_outputs')] = value;
this.update();
this.__outputs = value;
this.constructor.update(this);
}
get outputs() {
return this[Symbol.for('_outputs')];
return this.__outputs;
}
syncForm() {
const formData = new FormData(this.shadowRoot.getElementById('form'));
for (const type of ['ok', 'todo', 'tada', 'skip']) {
if (formData.get(type)) {
this.setAttribute(`show-${type}`, '');
} else {
this.removeAttribute(`show-${type}`);
}
}
connectedCallback() {
this.constructor.initializeOnce(this);
}
static onChange(evt) {
evt.target.getRootNode().host.syncForm();
}
disconnectedCallback() {
this.shadowRoot.removeEventListener('change', this.constructor.onChange);
}
connectedCallback() {
const symbol = Symbol.for('__initialized__');
if (!this[symbol]) {
this[symbol] = true;
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
static initializeOnce(target) {
if (!target.__initialized) {
target.__initialized = true;
target.shadowRoot.innerHTML = `
<style>

@@ -47,10 +62,8 @@ :host {

z-index: 1;
right: 0;
bottom: 0;
left: 0;
width: 100vw;
height: 45vh;
min-height: 260px;
box-sizing: border-box;
background-color: #111111;
color: white;
font-family: monospace;

@@ -70,2 +83,3 @@ }

line-height: 14px;
color: white;
background-color: #FF851B;

@@ -88,10 +102,8 @@ }

}
#form {
padding: 8px 12px;
opacity: .5;
#tag-line {
margin: auto 12px;
color: #8C8C8C;
cursor: default;
}
#form:hover {
opacity: 1;
}
#wrapper {
#body {
flex: 1;

@@ -102,2 +114,3 @@ overflow: auto;

flex-direction: column-reverse;
box-sizing: border-box;
}

@@ -112,2 +125,3 @@ #spacer {

padding: 0 12px;
cursor: default;
}

@@ -123,2 +137,8 @@ [output]:first-child {

}
[test],
[bail] {
display: block;
width: min-content;
cursor: pointer;
}
[it][ok]:not([directive]) {

@@ -143,111 +163,79 @@ color: #2ECC40;

}
[test] {
[test]:not([bail]) {
color: #0085ff;
}
[test],
[bail],
[test]:visited,
[bail]:visited {
text-decoration: none;
display: block;
width: min-content;
}
[test]:hover,
[bail]:hover {
text-decoration: underline;
}
#footer {
opacity: 0.5;
position: absolute;
bottom: 0;
right: 0;
color: #AAAAAA;
padding: 12px;
}
:host(:not([show-ok])) [it][ok]:not([directive]),
:host(:not([show-ok])) [it][ok]:not([directive]) + [yaml],
:host(:not([show-todo])) [it]:not([ok])[directive="todo"],
:host(:not([show-todo])) [it]:not([ok])[directive="todo"] + [yaml],
:host(:not([show-tada])) [it][ok][directive="todo"],
:host(:not([show-tada])) [it][ok][directive="todo"] + [yaml],
:host(:not([show-skip])) [it][ok][directive="skip"],
:host(:not([show-skip])) [it][ok][directive="skip"] + [yaml],
[filtered],
[filtered] + [yaml] {
display: none;
}
</style>
<div id="header">
<div id="result"></div>
<form id="form">
<label><input type="checkbox" name="ok" checked> ok</label>
<label><input type="checkbox" name="todo" checked> todo</label>
<label><input type="checkbox" name="tada" checked> tada</label>
<label><input type="checkbox" name="skip" checked> skip</label>
</form>
</div>
<div id="wrapper"><div id="spacer"></div><div id="container"></div></div>
<div id="footer">x-test - a simple, tap-compliant test runner for the browser.</div>
<div id="header"><div id="result"></div><div id="tag-line">${TAG_LINE}</div></div>
<div id="body"><div id="spacer"></div><div id="container"></div></div>
`;
this.setAttribute('ok', '');
this.setAttribute('testing', '');
this.syncForm();
this.shadowRoot.addEventListener('change', this.constructor.onChange);
target.setAttribute('ok', '');
target.setAttribute('testing', '');
}
}
update() {
if (this.outputs) {
const items = [];
const container = this.shadowRoot.getElementById('container');
for (const output of this.outputs.slice(container.children.length)) {
if (output.match(/^# https?:.*/)) {
const element = document.createElement('a');
element.href = output.replace('# ', '');
element.innerText = output;
element.setAttribute('output', '');
element.setAttribute('test', '');
items.push(element);
} else if (output.match(/^Bail out! https?:.*/)) {
const element = document.createElement('a');
element.href = output.replace('Bail out! ', '');
element.innerText = output;
element.setAttribute('output', '');
element.setAttribute('bail', '');
this.removeAttribute('ok');
items.push(element);
static parseOutput(output) {
const result = { tag: '', properties: {}, attributes: {}, failed: false, done: false };
result.properties.innerText = output;
if (output.match(/^# https?:.*/)) {
result.tag = 'a';
result.properties.href = output.replace('# ', '');
Object.assign(result.attributes, { output: '', test: '' });
} else if (output.match(/^Bail out! https?:.*/)) {
result.tag = 'a';
result.failed = true;
result.properties.href = output.replace('Bail out! ', '');
Object.assign(result.attributes, { output: '', test: '', bail: '' });
} else {
result.tag = 'div';
result.attributes.output = '';
if (output.match(/^# /)) {
result.attributes.diagnostic = '';
} else if (output.match(/^ok /)) {
Object.assign(result.attributes, { it: '', ok: '' });
if (output.match(/^[^#]* # SKIP/)) {
result.attributes.directive = 'skip';
} else if (output.match(/^[^#]* # TODO/)) {
result.attributes.directive = 'todo';
}
} else if (output.match(/^not ok /)) {
result.attributes.it = '';
if (output.match(/^[^#]* # TODO/)) {
result.attributes.directive = 'todo';
} else {
const element = document.createElement('div');
element.innerText = output;
element.setAttribute('output', '');
if (output.match(/^# /)) {
element.setAttribute('diagnostic', '');
} else if (output.match(/^\s*ok /)) {
element.setAttribute('it', '');
element.setAttribute('ok', '');
if (output.match(/^[^#]* # SKIP/)) {
element.setAttribute('directive', 'skip');
} else if (output.match(/^[^#]* # TODO/)) {
element.setAttribute('directive', 'todo');
}
} else if (output.match(/^\s*not ok /)) {
element.setAttribute('it', '');
if (output.match(/^[^#]* # TODO/)) {
element.setAttribute('directive', 'todo');
} else {
this.removeAttribute('ok');
}
} else if (output.match(/^\s*---/)) {
element.setAttribute('yaml', '');
} else if (output.match(/^TAP/)) {
element.setAttribute('version', '');
} else if (output.match(/^1\.\.\d*/)) {
element.setAttribute('plan', '');
this.removeAttribute('testing');
} else if (output.match(/Bail out!.*/)) {
this.removeAttribute('ok');
element.setAttribute('bail', '');
}
items.push(element);
result.failed = true;
}
} else if (output.match(/^ {2}---/)) {
result.attributes.yaml = '';
} else if (output.match(/^TAP/)) {
result.attributes.version = '';
} else if (output.match(/^1\.\.\d*/)) {
result.attributes.plan = '';
result.done = true;
} else if (output.match(/Bail out!.*/)) {
result.attributes.bail = '';
result.failed = true;
}
}
return result;
}
static update(target) {
if (target.outputs) {
const items = [];
const container = target.shadowRoot.getElementById('container');
for (const output of target.outputs.slice(container.children.length)) {
const { tag, properties, attributes, failed, done } = this.parseOutput(output);
const element = document.createElement(tag);
Object.assign(element, properties);
for (const [attribute, value] of Object.entries(attributes)) {
element.setAttribute(attribute, value);
}
if (done) {
target.removeAttribute('testing');
}
if (failed) {
target.removeAttribute('ok');
}
items.push(element);
}
container.append(...items);

@@ -262,15 +250,14 @@ }

}
static diagnostic(message) {
return `# ${message.replace(/\n/g, `\n# `)}`;
}
static testLine(ok, number, description, directive, reason) {
static testLine(ok, number, description, directive) {
description = description.replace(/\n/g, ' ');
const result = ok ? 'ok' : 'not ok';
let text = `${result} - ${number} ${description}`;
if (directive) {
reason = reason.replace(/\n/g, ' ');
text += ` # ${directive}${reason ? ` ${reason}` : ''}`;
}
return text;
const okText = ok ? 'ok' : 'not ok';
const directiveText = directive ? ` # ${directive}` : '';
return `${okText} - ${number} ${description}${directiveText}`;
}
static yaml(message, severity, data) {

@@ -287,5 +274,7 @@ let text = ' ---';

}
static bailOut(message) {
return `Bail out! ${message.replace(/\n/g, ' ')}`;
}
static plan(number) {

@@ -307,3 +296,3 @@ return `1..${number}`;

target.promises = [];
target.lastItId = null;
target.currentItId = null;
target.doneItIds = [];

@@ -337,2 +326,8 @@ }

static assert(assertion, message = 'assertion failed') {
if (!assertion) {
throw new Error(message);
}
}
static async waitFor(target, promise) {

@@ -375,30 +370,27 @@ if (!target.bailed) {

static async it(target, directive, reason, description, callback, interval) {
static async it(target, directive, description, callback, interval) {
// TODO: crude way to protect against accidental directives in description.
description.replace(/#/g, '*');
if (!(callback instanceof Function)) {
throw new Error(`Callback must be a function (got ${callback}).`);
}
if (!target.bailed) {
const itId = this.uuidv4();
const { lastItId } = target;
target.lastItId = itId;
const lastItId = target.currentItId;
target.currentItId = itId;
this.waitFor(target, this.promiseIt(target, itId));
if (
// TODO: This seems like a potential eslint bug? It's not clear why
// this would would be a constant condition.
// eslint-disable-next-line no-constant-condition
lastItId === null ||
target.doneItIds.includes(lastItId) ||
((await this.promiseIt(target, lastItId)) || true)
) {
const { testId: parentTestId } = target;
this.post('x-test-it-started', { itId, parentTestId });
const data = { itId, description, directive, reason, ok: true };
try {
if (directive !== 'SKIP') {
await Promise.race([callback(), this.timeout(interval)]);
}
} catch (err) {
data.ok = false;
data.error = this.createError(err);
} finally {
this.post('x-test-it-ended', data);
target.doneItIds.push(itId);
if (lastItId && !target.doneItIds.includes(lastItId)) {
await this.promiseIt(target, lastItId);
}
this.post('x-test-it-started', { itId, parentTestId: target.testId });
const data = { itId, description, directive, ok: true };
try {
if (directive !== 'SKIP') {
await Promise.race([callback(), this.timeout(interval)]);
}
} catch (error) {
Object.assign(data, { ok: false, error: this.createError(error) });
} finally {
this.post('x-test-it-ended', data);
target.doneItIds.push(itId);
}

@@ -425,6 +417,6 @@ }

// TODO: do we need to filter out untrusted origins here?
top.postMessage(data ? { type, data } : { type }, '*');
top.postMessage({ type, data }, '*');
}
static yamlIt(ok, directive, reason, error) {
static yamlIt(ok, directive, error) {
const yaml = { message: 'ok', severity: 'comment', data: {} };

@@ -466,6 +458,3 @@ if (ok) {

return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
(c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
);

@@ -497,4 +486,3 @@ }

target.outputs = [...target.outputs, string];
// eslint-disable-next-line no-console
console.log(string);
console.log(string); // eslint-disable-line no-console
if (target.reporter) {

@@ -561,9 +549,6 @@ target.reporter.outputs = target.outputs;

Object.assign(target.its[data.itId], data);
const { itId, ok, description, directive, reason, error } = data;
const { itId, ok, description, directive, error } = data;
const number = target.itIds.indexOf(itId) + 1;
this.output(
target,
Tap.testLine(ok, number, description, directive, reason)
);
const yaml = this.yamlIt(ok, directive, reason, error);
this.output(target, Tap.testLine(ok, number, description, directive));
const yaml = this.yamlIt(ok, directive, error);
// We may choose to output these later...

@@ -605,14 +590,11 @@ if (yaml.severity !== 'comment') {

const ranges = [];
const state = { saw: set.has(0), start: 0 };
const state = { used: set.has(0), start: 0 };
for (let index = 0; index < text.length; index++) {
const saw = set.has(index);
if (saw !== state.saw) {
ranges.push({ saw: state.saw, start: state.start, end: index });
Object.assign(state, { saw, start: index });
const used = set.has(index);
if (used !== state.used) {
ranges.push({ used: state.used, start: state.start, end: index });
Object.assign(state, { used, start: index });
}
}
ranges.push({ saw: state.saw, start: state.start, end: text.length });
const used = set.size;
const total = text.length;
const percent = used / total * 100;
ranges.push({ used: state.used, start: state.start, end: text.length });
let output = '';

@@ -624,4 +606,4 @@ let lineNumber = 1;

.split('\n')
.map((line, iii) => lineNumber === 1 || iii > 0 ? `${String(lineNumber++ + (range.saw ? '' : ' !')).padEnd(8, ' ')}| ${line}` : line);
if (range.saw) {
.map((line, iii) => lineNumber === 1 || iii > 0 ? `${String(lineNumber++ + (range.used ? '' : ' !')).padEnd(8, ' ')}| ${line}` : line);
if (range.used) {
if (lines.length > 3) {

@@ -635,4 +617,5 @@ lines = [...lines.slice(0, 1), '\u2026', ...lines.slice(-1)];

}
output += range.saw ? `${lines.join('\n')}` : `${lines.join('\n')}`;
output += range.used ? `${lines.join('\n')}` : `${lines.join('\n')}`;
}
const percent = set.size / text.length * 100;
const ok = percent >= goal;

@@ -656,12 +639,7 @@ return { ok, percent, output };

const el = document.createElement('iframe');
el.id = testId;
el.src = href;
el.style.border = 'none';
el.style.backgroundColor = 'white';
el.style.height = '100vh';
el.style.width = '100vw';
el.style.position = 'fixed';
el.style.zIndex = '0';
el.style.top = '0';
el.style.left = '0';
Object.assign(el, { id: testId, src: href });
Object.assign(el.style, {
border: 'none', backgroundColor: 'white', height: '100vh',
width: '100vw', position: 'fixed', zIndex: '0', top: '0', left: '0',
});
el.addEventListener('error', () => {

@@ -715,37 +693,2 @@ const error = new Error(`${target.href} failed to load ${href}`);

function cover(relativePath, goal) {
Test.cover(_test, relativePath, goal);
}
function assert(assertion, message) {
if (!assertion) {
throw new Error(message || 'assertion failed');
}
}
function test(href) {
// Initializes a new test script in an iframe.
Test.test(_test, href);
}
function it(description, callback, interval) {
// Registers a new test. Callback is not run immediately.
Test.it(_test, null, null, description, callback, interval);
}
function skip(reason, description, callback, interval) {
// Count test as passed and do not attempt to run.
Test.it(_test, 'SKIP', reason, description, callback, interval);
}
function todo(reason, description, callback, interval) {
// Expect that test is failing. Test will run to confirm this.
Test.it(_test, 'TODO', reason, description, callback, interval);
}
function waitFor(promise) {
// Don't end test before this promise settles (test bails if promise throws).
Test.waitFor(_test, promise);
}
// When we boot a new test, we check if we were instantiated or if we're root.

@@ -756,3 +699,2 @@ const _isRoot = frameElement === null || !frameElement.id;

// Make sure uncaught errors and unhandled rejections cause failures.
addEventListener('error', evt => {

@@ -759,0 +701,0 @@ evt.preventDefault();

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc