chore(test): move from deprecated karma to web-test-runner for browser tests (#1052)

pull/1056/head
Jan van Brügge 1 year ago committed by GitHub
parent 6ff10c720b
commit 0a4fb34ffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

2
.gitignore vendored

@ -23,3 +23,5 @@
/test-bundles/unit/jsx.js
/test-bundles/unit/style.js
/test-bundles/unit/thunk.js
local.log

@ -1,29 +1,29 @@
module.exports = {
export default {
// Latest mainstream
BS_Chrome_Current: {
base: "BrowserStack",
browser: "chrome",
browserName: "chrome",
browser_version: "latest",
os: "Windows",
os_version: "10",
},
BS_Firefox_Current: {
base: "BrowserStack",
browser: "firefox",
browserName: "firefox",
browser_version: "latest",
os: "Windows",
os_version: "10",
},
BS_Safari_Current: {
base: "BrowserStack",
browser: "safari",
browserName: "safari",
browser_version: "latest",
os: "OS X",
os_version: "Big Sur",
},
BS_Android_8: {
base: "BrowserStack",
browser: "Android",
browserName: "Android",
device: "Google Pixel 2",
os: "Android",
os_version: "8.0",
@ -33,21 +33,21 @@ module.exports = {
// Older mainstream
BS_Chrome_50: {
base: "BrowserStack",
browser: "chrome",
browserName: "chrome",
browser_version: "50",
os: "Windows",
os_version: "10",
},
BS_Firefox_52: {
base: "BrowserStack",
browser: "firefox",
browserName: "firefox",
browser_version: "52",
os: "Windows",
os_version: "10",
},
BS_Safari_10: {
base: "BrowserStack",
browser: "safari",
browserName: "safari",
browser_version: "10.1",
os: "OS X",
os_version: "Sierra",
@ -64,7 +64,7 @@ module.exports = {
},
BS_iphone_10: {
base: "BrowserStack",
browser: "Mobile Safari",
browserName: "Mobile Safari",
browser_version: null,
device: "iPhone 7",
real_mobile: true,
@ -73,14 +73,14 @@ module.exports = {
},
BS_MS_Edge: {
base: "BrowserStack",
browser: "edge",
browserName: "edge",
browser_version: "latest",
os: "Windows",
os_version: "10",
},
BS_IE_11: {
base: "BrowserStack",
browser: "ie",
browserName: "ie",
browser_version: "11.0",
os: "Windows",
os_version: "7",

@ -1,78 +0,0 @@
const ci = !!process.env.CI;
const watch = !!process.env.WATCH;
const live = !!process.env.LIVE;
const es5 = !!process.env.ES5;
const ip = "bs-local.com";
const browserstack = require("./browserstack-karma.js");
// https://www.browserstack.com/open-source (text search "parallels")
// Instead of the 5 available we only use 2, so two commits can run CI at the same time
const BROWSERSTACK_OPEN_SOURCE_CONCURRENCY = 2;
const getBrowserstackBrowsers = () =>
Object.keys(browserstack).filter((k) => !!browserstack[k].es5 === es5);
const browsers = ci
? getBrowserstackBrowsers()
: live
? undefined
: watch
? ["Chrome"]
: ["ChromeHeadless", "FirefoxHeadless"];
module.exports = function (config) {
config.set({
basePath: ".",
frameworks: ["mocha", "karma-typescript"],
// list of files / patterns to load in the browser
files: process.env.FILES_PATTERN.split(",")
.map((p) => ({ pattern: p }))
.concat({ pattern: "src/**/*.ts" }),
preprocessors: {
"**/*.ts": "karma-typescript",
"**/*.tsx": "karma-typescript",
},
plugins: [
"karma-mocha",
"karma-typescript",
"karma-mocha-reporter",
"karma-chrome-launcher",
"karma-firefox-launcher",
"karma-browserstack-launcher",
],
hostname: ci ? ip : "localhost",
karmaTypescriptConfig: {
compilerOptions: {
...require("./tsconfig.json").compilerOptions,
...require("./test/tsconfig.json").compilerOptions,
sourceMap: false,
inlineSourceMap: true,
target: es5 ? "es5" : "es6",
},
bundlerOptions: {
sourceMap: true,
},
include: process.env.FILES_PATTERN.split(",").concat("src/**/*.ts"),
},
browserStack: {
name: "Snabbdom",
retryLimit: 1,
},
client: {
captureConsole: true,
},
customLaunchers: browserstack,
reporters: ["karma-typescript", "mocha", "BrowserStack"],
mochaReporter: {
showDiff: true,
},
port: 9876,
colors: true,
autoWatch: true,
browsers: browsers,
singleRun: !watch && !live,
concurrency: ci ? BROWSERSTACK_OPEN_SOURCE_CONCURRENCY : Infinity,
});
};

6258
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -22,8 +22,8 @@
"engines": {
"node": ">=8.3.0"
},
"main": "build/snabbdom.cjs.js",
"module": "build/index.js",
"type": "module",
"main": "build/index.js",
"types": "build/index.d.ts",
"sideEffects": false,
"scripts": {
@ -33,19 +33,22 @@
"format": "prettier --write .",
"prepare": "husky install",
"lint": "eslint --ext .ts,.tsx,.js --ignore-path .gitignore .",
"unit": "cross-env FILES_PATTERN=\"test/unit/*.ts,test/unit/*.tsx\" karma start karma.conf.js",
"unit": "web-test-runner \"test/unit/*.{ts,tsx}\" --node-resolve --coverage",
"release": "npm run test && release-it",
"test:ci": "npm test && cross-env ES5=true npm run unit",
"test": "npm run build && npm run lint && npm run unit"
"test": "npm run build && npm run lint && npm run unit",
"test:watch": "web-test-runner \"test/unit/*.{tsx,tsx}\" --node-resolve --watch"
},
"devDependencies": {
"@esm-bundle/chai": "^4.3.4-fix.0",
"@release-it/conventional-changelog": "^7.0.2",
"@types/chai": "4.3.9",
"@types/lodash.shuffle": "4.2.8",
"@types/lodash-es": "^4.17.10",
"@types/mocha": "10.0.3",
"@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"chai": "4.3.10",
"@web/dev-server-esbuild": "^0.4.3",
"@web/test-runner": "^0.17.2",
"@web/test-runner-browserstack": "^0.6.2",
"commithelper": "^1.2.0",
"conventional-changelog-angular": "^7.0.0",
"cross-env": "7.0.3",
@ -55,15 +58,8 @@
"eslint-plugin-markdown": "3.0.1",
"eslint-plugin-node": "11.1.0",
"husky": "8.0.3",
"karma": "6.4.2",
"karma-browserstack-launcher": "1.6.0",
"karma-chrome-launcher": "3.2.0",
"karma-firefox-launcher": "2.1.2",
"karma-mocha": "2.0.1",
"karma-mocha-reporter": "^2.2.5",
"karma-typescript": "^5.5.4",
"lint-staged": "^15.0.2",
"lodash.shuffle": "4.2.0",
"lodash-es": "^4.17.21",
"mocha": "10.2.0",
"prettier": "^3.0.3",
"release-it": "^16.2.1",

@ -1,39 +1,46 @@
// core
export { DOMAPI, htmlDomApi } from "./htmldomapi";
export { init, Options } from "./init";
export { ThunkData, Thunk, ThunkFn, thunk } from "./thunk";
export { Key, VNode, VNodeData, vnode } from "./vnode";
export { htmlDomApi } from "./htmldomapi";
export { init } from "./init";
export { thunk } from "./thunk";
export { vnode } from "./vnode";
export type { DOMAPI } from "./htmldomapi";
export type { Options } from "./init";
export type { ThunkData, Thunk, ThunkFn } from "./thunk";
export type { Key, VNode, VNodeData } from "./vnode";
// helpers
export { AttachData, attachTo } from "./helpers/attachto";
export { attachTo } from "./helpers/attachto";
export { array, primitive } from "./is";
export { toVNode } from "./tovnode";
export {
export { h, fragment } from "./h";
export type { AttachData } from "./helpers/attachto";
export type {
VNodes,
VNodeChildElement,
ArrayOrElement,
VNodeChildren,
h,
fragment,
} from "./h";
// types
export * from "./hooks";
export { Module } from "./modules/module";
export type { Module } from "./modules/module";
// modules
export { Attrs, attributesModule } from "./modules/attributes";
export { Classes, classModule } from "./modules/class";
export { Dataset, datasetModule } from "./modules/dataset";
export { On, eventListenersModule } from "./modules/eventlisteners";
export { Props, propsModule } from "./modules/props";
export { VNodeStyle, styleModule } from "./modules/style";
export { attributesModule } from "./modules/attributes";
export { classModule } from "./modules/class";
export { datasetModule } from "./modules/dataset";
export { eventListenersModule } from "./modules/eventlisteners";
export { propsModule } from "./modules/props";
export { styleModule } from "./modules/style";
export type { Attrs } from "./modules/attributes";
export type { Classes } from "./modules/class";
export type { Dataset } from "./modules/dataset";
export type { On } from "./modules/eventlisteners";
export type { Props } from "./modules/props";
export type { VNodeStyle } from "./modules/style";
// JSX
export {
JsxVNodeChild,
JsxVNodeChildren,
FunctionComponent,
jsx,
Fragment,
} from "./jsx";
export { jsx, Fragment } from "./jsx";
export type { JsxVNodeChild, JsxVNodeChildren, FunctionComponent } from "./jsx";

@ -2,14 +2,6 @@
import { Key, vnode, VNode, VNodeData } from "./vnode";
import { h, ArrayOrElement } from "./h";
// See https://www.typescriptlang.org/docs/handbook/jsx.html#type-checking
namespace JSXInternal {
export type Element = VNode;
export interface IntrinsicElements {
[elemName: string]: VNodeData;
}
}
// for conditional rendering we support boolean child element e.g cond && <tag />
export type JsxVNodeChild =
| VNode
@ -22,7 +14,7 @@ export type JsxVNodeChildren = ArrayOrElement<JsxVNodeChild>;
export type FunctionComponent = (
props: { [prop: string]: any } | null,
children?: VNode[]
children?: VNode[],
) => VNode;
export function Fragment(
@ -42,7 +34,7 @@ export function Fragment(
undefined,
undefined,
flatChildren[0].text,
undefined
undefined,
);
} else {
return vnode(undefined, data ?? {}, flatChildren, undefined, undefined);
@ -51,7 +43,7 @@ export function Fragment(
function flattenAndFilter(
children: JsxVNodeChildren[],
flattened: VNode[]
flattened: VNode[],
): VNode[] {
for (const child of children) {
// filter out falsey children, except 0 since zero can be a valid value e.g inside a chart
@ -69,7 +61,7 @@ function flattenAndFilter(
typeof child === "boolean"
) {
flattened.push(
vnode(undefined, undefined, undefined, String(child), undefined)
vnode(undefined, undefined, undefined, String(child), undefined),
);
} else {
flattened.push(child);
@ -106,6 +98,10 @@ export function jsx(
}
}
// See https://www.typescriptlang.org/docs/handbook/jsx.html#type-checking
export namespace jsx {
export import JSX = JSXInternal; // eslint-disable-line @typescript-eslint/no-unused-vars
export type Element = VNode;
export interface IntrinsicElements {
[elemName: string]: VNodeData;
}
}

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { init, RemoveHook, attachTo, h } from "../../src/index";
@ -82,7 +82,7 @@ describe("attachTo", function () {
h("div", "Some element"),
attachTo(
elm,
h("div#attached", { hook: { remove: rm } }, "First text")
h("div#attached", { hook: { remove: rm } }, "First text"),
),
]),
]);

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { init, attributesModule, h } from "../../src/index";
@ -49,7 +49,7 @@ describe("attributes", function () {
elm = patch(vnode0, vnode1).elm;
assert.strictEqual(
elm.getAttributeNS("http://www.w3.org/1999/xlink", "href"),
"#foo"
"#foo",
);
});
it("should not touch class nor id fields", function () {

@ -1,5 +1,5 @@
import { assert } from "chai";
import shuffle from "lodash.shuffle";
import { assert } from "@esm-bundle/chai";
import { shuffle } from "lodash-es";
import {
init,
@ -28,7 +28,7 @@ const hasSvgClassList = "classList" in SVGElement.prototype;
const patch = init(
[classModule, propsModule, eventListenersModule],
undefined,
{ experimental: { fragments: true } }
{ experimental: { fragments: true } },
);
function prop<T>(name: string) {
@ -145,13 +145,13 @@ describe("snabbdom", function () {
vnode0,
h("svg", [
h("foreignObject", [h("div", ["I am HTML embedded in SVG"])]),
])
]),
).elm;
assert.strictEqual(elm.namespaceURI, SVGNamespace);
assert.strictEqual(elm.firstChild.namespaceURI, SVGNamespace);
assert.strictEqual(
elm.firstChild.firstChild.namespaceURI,
XHTMLNamespace
XHTMLNamespace,
);
// verify that svg tag with extra selectors gets svg namespace
@ -171,7 +171,7 @@ describe("snabbdom", function () {
it("receives classes in class property", function () {
elm = patch(
vnode0,
h("i", { class: { am: true, a: true, class: true, not: false } })
h("i", { class: { am: true, a: true, class: true, not: false } }),
).elm;
assert(elm.classList.contains("am"));
assert(elm.classList.contains("a"));
@ -198,7 +198,7 @@ describe("snabbdom", function () {
h("g", {
class: { am: true, a: true, class: true, not: false, too: true },
}),
])
]),
).elm;
assert(elm.firstChild.classList.contains("am"));
assert(elm.firstChild.classList.contains("a"));
@ -209,12 +209,12 @@ describe("snabbdom", function () {
it("handles classes from both selector and property", function () {
elm = patch(
vnode0,
h("div", [h("i.has", { class: { classes: true } })])
h("div", [h("i.has", { class: { classes: true } })]),
).elm;
assert(elm.firstChild.classList.contains("has"), "has `has` class");
assert(
elm.firstChild.classList.contains("classes"),
"has `classes` class"
"has `classes` class",
);
});
it("can create elements with text content", function () {
@ -241,11 +241,11 @@ describe("snabbdom", function () {
frame.srcdoc = "<div>Thing 1</div>";
frame.onload = function () {
const div0 = frame.contentDocument!.body.querySelector(
"div"
"div",
) as HTMLDivElement;
patch(div0, h("div", "Thing 2"));
const div1 = frame.contentDocument!.body.querySelector(
"div"
"div",
) as HTMLDivElement;
assert.strictEqual(div1.textContent, "Thing 2");
frame.remove();
@ -339,11 +339,11 @@ describe("snabbdom", function () {
style: { height: "100px", overflowY: "scroll" },
props: { scrollTop },
},
[h("div", { style: { height: "200px" } })]
[h("div", { style: { height: "200px" } })],
);
const vnode1 = view(0);
const mountPoint = document.body.appendChild(
document.createElement("div")
document.createElement("div"),
);
const { elm } = patch(mountPoint, vnode1);
if (!(elm instanceof HTMLDivElement)) throw new Error();
@ -405,7 +405,7 @@ describe("snabbdom", function () {
if ("customElements" in window) {
describe("customized built-in element", function () {
const isSafari = /^((?!chrome|android).)*safari/i.test(
navigator.userAgent
navigator.userAgent,
);
if (!isSafari) {
@ -473,7 +473,7 @@ describe("snabbdom", function () {
{},
[h("div#id.class", [h("span", "Hi")])],
undefined,
prevElm as any
prevElm as any,
);
elm = patch(toVNode(prevElm), nextVNode).elm;
assert.strictEqual(elm, prevElm);
@ -786,7 +786,7 @@ describe("snabbdom", function () {
const vnode1 = h("span", [Symbol()].map(spanNum));
const vnode2 = h(
"span",
[Symbol("1"), Symbol("2"), Symbol("3")].map(spanNum)
[Symbol("1"), Symbol("2"), Symbol("3")].map(spanNum),
);
elm = patch(vnode0, vnode1).elm;
assert.equal(elm.children.length, 1);
@ -846,7 +846,7 @@ describe("snabbdom", function () {
"span",
arr.map(function (n) {
return spanNumWithOpacity(n, "1");
})
}),
);
const shufArr = shuffle(arr.slice(0));
let elm: HTMLDivElement | HTMLSpanElement =
@ -860,13 +860,13 @@ describe("snabbdom", function () {
"span",
arr.map(function (n) {
return spanNumWithOpacity(shufArr[n], opacities[n]);
})
}),
);
elm = patch(vnode1, vnode2).elm as HTMLSpanElement;
for (i = 0; i < elms; ++i) {
assert.strictEqual(
elm.children[i].innerHTML,
shufArr[i].toString()
shufArr[i].toString(),
);
const opacity = (elm.children[i] as HTMLSpanElement).style.opacity;
assert.strictEqual(opacities[i].indexOf(opacity), 0);
@ -878,8 +878,8 @@ describe("snabbdom", function () {
const vnode2 = h(
"i",
[null, 2, undefined, null, 1, 0, null, 5, 4, null, 3, undefined].map(
spanNum
)
spanNum,
),
);
elm = patch(vnode0, vnode1).elm;
assert.strictEqual(elm.children.length, 6);
@ -935,7 +935,7 @@ describe("snabbdom", function () {
map(inner, elm.children),
arr.filter(function (x) {
return x != null;
})
}),
);
}
});

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { datasetModule, init, h, toVNode } from "../../src/index";

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { VNode, init, eventListenersModule, h } from "../../src/index";
@ -34,7 +34,7 @@ describe("event listeners", function () {
},
},
},
[h("a", "Click my parent")]
[h("a", "Click my parent")],
);
const vnode2 = h(
"div",
@ -45,7 +45,7 @@ describe("event listeners", function () {
},
},
},
[h("a", "Click my parent")]
[h("a", "Click my parent")],
);
elm = patch(vnode0, vnode1).elm;
elm.click();

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { init, h, attributesModule } from "../../src/index";
@ -27,7 +27,7 @@ describe("svg", function () {
{
attrs: { "xlink:href": testUrl },
},
[]
[],
),
]);

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { jsx, Fragment, init } from "../../src/index";
describe("snabbdom", function () {
@ -565,7 +565,7 @@ describe("snabbdom", function () {
assert.strictEqual(vnode0.elm?.nodeType, document.DOCUMENT_FRAGMENT_NODE);
assert.strictEqual(
vnode0.elm?.textContent,
"Nested children will be flattened"
"Nested children will be flattened",
);
const vnode1 = (
@ -582,7 +582,7 @@ describe("snabbdom", function () {
assert.strictEqual((vnode1.elm as HTMLElement).tagName, "DIV");
assert.strictEqual(
vnode1.elm?.textContent,
"Nested child nodes will be patched"
"Nested child nodes will be patched",
);
const vnode2 = (

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { init, styleModule, h, toVNode } from "../../src/index";
@ -143,7 +143,7 @@ describe("style", function () {
remove: { transform: "translateY(100%)" } as any,
},
},
["A button"]
["A button"],
);
const vnode1 = h("div.parent", {}, [btn]);
const vnode2 = h("div.parent", {}, [null]);

@ -1,4 +1,4 @@
import { assert } from "chai";
import { assert } from "@esm-bundle/chai";
import { init, h, thunk, VNode } from "../../src/index";
@ -172,7 +172,7 @@ describe("thunk", function () {
return h(
"span",
{ key: "num", hook: { destroy: destroyHook } },
`Number is ${n}`
`Number is ${n}`,
);
}
const vnode1 = h("div", [
@ -194,7 +194,7 @@ describe("thunk", function () {
return h(
"span",
{ key: "num", hook: { remove: hook } },
`Number is ${n}`
`Number is ${n}`,
);
}
const vnode1 = h("div", [

@ -7,6 +7,7 @@
"strictFunctionTypes": false,
"moduleResolution": "node",
"target": "es6",
"isolatedModules": true,
"outDir": "build"
},
"include": ["./src/index.ts"]

@ -0,0 +1,32 @@
import { esbuildPlugin } from "@web/dev-server-esbuild";
import { browserstackLauncher } from "@web/test-runner-browserstack";
import browsers from "./browserstack-browsers.js";
const ci = !!process.env.CI;
const sharedCapabilities = {
"browserstack.user": process.env.BROWSER_STACK_USERNAME,
"browserstack.key": process.env.BROWSER_STACK_ACCESS_KEY,
project: "snabbdom",
name: "CI",
build: `build ${process.env.GITHUB_RUN_NUMBER || "unknown"}`,
};
export default {
concurrentBrowsers: 2,
concurrency: 6,
browsers: !ci
? undefined
: Object.values(browsers).map((cap) =>
browserstackLauncher({
capabilities: {
...sharedCapabilities,
...cap,
},
}),
),
files: ["src/**/*.ts", "test/unit/*.ts", "test/unit/*.tsx"],
plugins: [
esbuildPlugin({ ts: true, tsx: true, tsconfig: "./test/tsconfig.json" }),
],
};
Loading…
Cancel
Save