From be316dec98aeee66a09b2534e8cbb5b0f5b18c7e Mon Sep 17 00:00:00 2001 From: Chris Burnell Date: Thu, 8 Aug 2024 13:54:26 +0800 Subject: [PATCH] test: use linkedom to test web component functionality --- .github/workflows/npm-publish.yml | 3 +- .npmignore | 3 +- .prettierrc | 3 +- package-lock.json | 203 ++++++++++++++++++++++++++++++ package.json | 9 +- svg-sparkline.test.js | 180 ++++++++++++++++++++++++++ 6 files changed, 396 insertions(+), 5 deletions(-) create mode 100644 package-lock.json create mode 100644 svg-sparkline.test.js diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 0c159af..71f0c0e 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -16,7 +16,8 @@ jobs: with: node-version: 20 registry-url: https://registry.npmjs.org/ - - run: npm install + - run: npm ci + - run: node --test - if: ${{ github.event.release.tag_name != '' && env.NPM_PUBLISH_TAG != '' }} run: npm publish --provenance --access=public --tag=${{ env.NPM_PUBLISH_TAG }} env: diff --git a/.npmignore b/.npmignore index 3ed9a90..7e77da1 100644 --- a/.npmignore +++ b/.npmignore @@ -5,5 +5,6 @@ .eslintrc.json .prettierrc .prettierignore +svg-sparkline.test.js assets -demo.html \ No newline at end of file +demo.html diff --git a/.prettierrc b/.prettierrc index cef22be..7565d91 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,4 @@ { - "tabWidth": 4 + "tabWidth": 4, + "trailingComma": "all" } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e3d2488 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,203 @@ +{ + "name": "@chrisburnell/svg-sparkline", + "version": "1.0.12", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@chrisburnell/svg-sparkline", + "version": "1.0.12", + "funding": [ + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/chrisburnell" + }, + { + "type": "github", + "url": "https://github.com/sponsors/chrisburnell" + } + ], + "license": "MIT", + "devDependencies": { + "linkedom": "^0.18.4" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/linkedom": { + "version": "0.18.4", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.4.tgz", + "integrity": "sha512-JhLErxMIEOKByMi3fURXgI1fYOzR87L1Cn0+MI9GlMckFrqFZpV1SUGox1jcKtsKN3y6JgclcQf0FzZT//BuGw==", + "dev": true, + "license": "ISC", + "dependencies": { + "css-select": "^5.1.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^9.1.0", + "uhyphen": "^0.2.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/uhyphen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json index 5f3099f..93cd043 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ }, "main": "svg-sparkline.js", "scripts": { - "start": "npx http-server ." + "start": "npx http-server .", + "test": "node --test" }, "keywords": [ "component", @@ -47,5 +48,9 @@ "custom elements", "web", "web components" - ] + ], + "devDependencies": { + "linkedom": "^0.18.4" + }, + "type": "module" } diff --git a/svg-sparkline.test.js b/svg-sparkline.test.js new file mode 100644 index 0000000..e6f735a --- /dev/null +++ b/svg-sparkline.test.js @@ -0,0 +1,180 @@ +import { parseHTML } from "linkedom"; +import assert from "node:assert/strict"; +import { afterEach, before, describe, it } from "node:test"; + +describe(" Web Component", () => { + let window, document, customElements, HTMLElement, DocumentFragment, Event; + + const defaultBody = ``; + + before(async () => { + window = global.window = parseHTML(` + + + + ${defaultBody} + + + `); + DocumentFragment = global.DocumentFragment = window.DocumentFragment; + document = global.document = window.document; + customElements = global.customElements = window.customElements; + Event = global.Event = window.Event; + HTMLElement = global.HTMLElement = window.HTMLElement; + + await import("./svg-sparkline.js"); + }); + + afterEach(() => { + document.body.innerHTML = defaultBody; + }); + + it("Should be defined in the customElements registry", () => { + assert.strictEqual(!!customElements.get("svg-sparkline"), true); + }); + + it("Should be able to set the line to be curved", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("curve", "true"); + + const pathElement = customElement.shadowRoot.querySelector( + "svg:first-of-type path", + ); + assert.strictEqual(/C/.test(pathElement.getAttribute("d")), true); + }); + + it("Should be able to set a start label", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("start-label", "Start"); + + const spanElement = + customElement.shadowRoot.querySelector("span:first-of-type"); + assert.strictEqual(spanElement.innerText, "Start"); + }); + + it("Should be able to set an end label", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("end-label", "End"); + + const spanElement = + customElement.shadowRoot.querySelector("span:last-of-type"); + assert.strictEqual(spanElement.innerText, "End"); + }); + + it("Should be able to set a start and end label", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("start-label", "Start"); + customElement.setAttribute("end-label", "End"); + + const spanElementFirst = + customElement.shadowRoot.querySelector("span:first-of-type"); + assert.strictEqual(spanElementFirst.innerText, "Start"); + const spanElementSecond = + customElement.shadowRoot.querySelector("span:last-of-type"); + assert.strictEqual(spanElementSecond.innerText, "End"); + }); + + it("Should be able to set the colour of the line", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("color", "red"); + + const pathElement = customElement.shadowRoot.querySelector( + "svg:first-of-type path", + ); + assert.strictEqual(pathElement.getAttribute("stroke"), "red"); + }); + + it("Should be able to set a gradient below the line", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("gradient", "true"); + + const linearGradientElement = customElement.shadowRoot.querySelector( + "svg:first-of-type defs linearGradient", + ); + assert.strictEqual(!!linearGradientElement, true); + }); + + it("Should be able to set a gradient colour", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("gradient", "true"); + customElement.setAttribute("gradient-color", "red"); + + const stopElement = customElement.shadowRoot.querySelector( + "svg:first-of-type defs linearGradient stop:first-of-type", + ); + assert.strictEqual(stopElement.getAttribute("stop-color"), "red"); + }); + + it("Should be able to set a fill below the line", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("fill", "true"); + + const pathElement = customElement.shadowRoot.querySelector( + "svg:first-of-type path:first-of-type", + ); + assert.notStrictEqual(pathElement.getAttribute("fill"), "transparent"); + assert.strictEqual(pathElement.getAttribute("stroke"), "transparent"); + }); + + it("Should be able to set a fill colour", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("fill", "true"); + customElement.setAttribute("fill-color", "red"); + + const pathElement = customElement.shadowRoot.querySelector( + "svg:first-of-type path:first-of-type", + ); + assert.strictEqual(pathElement.getAttribute("fill"), "red"); + assert.strictEqual(pathElement.getAttribute("stroke"), "transparent"); + }); + + it("Should be able to set the colour of the endpoint", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("endpoint-color", "red"); + + const circleElement = customElement.shadowRoot.querySelector( + "svg:last-of-type circle", + ); + assert.strictEqual(circleElement.getAttribute("fill"), "red"); + }); + + it("Should be able to remove the endpoint", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("endpoint", "false"); + + const circleElement = customElement.shadowRoot.querySelector( + "svg:last-of-type circle", + ); + assert.strictEqual(!!circleElement, false); + }); + + it("Should be able to set the radius of the endpoint", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("endpoint-width", "10"); + + const pathElement = customElement.shadowRoot.querySelector( + "svg:last-of-type circle", + ); + assert.strictEqual(pathElement.getAttribute("r"), "5"); + }); + + it("Should be able to set the width of the line", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("line-width", "10"); + + const pathElement = customElement.shadowRoot.querySelector( + "svg:first-of-type path", + ); + assert.strictEqual(pathElement.getAttribute("stroke-width"), "10"); + }); + + it("Should be able to set the dimensions of the custom element", () => { + const customElement = document.querySelector("svg-sparkline"); + customElement.setAttribute("width", "300"); + customElement.setAttribute("height", "100"); + + const svgElement = customElement.shadowRoot.querySelector("svg"); + assert.strictEqual(svgElement.getAttribute("width"), "300px"); + assert.strictEqual(svgElement.getAttribute("height"), "100px"); + }); +});