diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index c6231ea1..00000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import tseslint from 'typescript-eslint'; - -export default tseslint.config(...tseslint.configs.recommended); diff --git a/konva-node/demo.js b/konva-node/demo.js deleted file mode 100644 index be94f463..00000000 --- a/konva-node/demo.js +++ /dev/null @@ -1,77 +0,0 @@ -import fs from 'fs'; - -// relative path here -// but you will need just require('konva-node'); -import Konva from '../'; - -// Create stage. Container parameter is not required in NodeJS. -var stage = new Konva.Stage({ - width: 100, - height: 100, -}); - -var layer = new Konva.Layer(); -stage.add(layer); - -var rect = new Konva.Rect({ - width: 100, - height: 100, - x: 50, - y: 50, - fill: 'white', -}); -var text = new Konva.Text({ - text: 'Generated inside node js', - x: 20, - y: 20, - fill: 'black', -}); -layer.add(rect).add(text); -layer.draw(); -stage.setSize({ - width: 200, - height: 200, -}); - -// check tween works -var tween = new Konva.Tween({ - node: rect, - duration: 1, - x: -50, -}); -tween.play(); - -// After tween we want to convert stage to dataURL -setTimeout(function () { - stage.toDataURL({ - callback: function (data) { - // Then add result to stage - var img = new Konva.window.Image(); - img.onload = function () { - var image = new Konva.Image({ - image: img, - x: 10, - y: 50, - }); - layer.add(image); - layer.draw(); - // save stage image as file - stage.toDataURL({ - callback: function (data) { - var base64Data = data.replace(/^data:image\/png;base64,/, ''); - fs.writeFile('./out.png', base64Data, 'base64', function (err) { - err && console.log(err); - console.log('See out.png'); - }); - // now try to create image from url - Konva.Image.fromURL(data, () => { - console.log('image loaded'); - // shoul'd throw - }); - }, - }); - }; - img.src = data; - }, - }); -}, 1050); diff --git a/konva-node/index.js b/konva-node/index.js deleted file mode 100644 index 4b6b3c77..00000000 --- a/konva-node/index.js +++ /dev/null @@ -1,39 +0,0 @@ -var Konva = require('konva'); -var canvas = require('canvas'); - -// mock window -Konva.window = { - Image: canvas.Image, - devicePixelRatio: 1, -}; -// mock document -Konva.document = { - createElement: function () {}, - documentElement: { - addEventListener: function () {}, - }, -}; - -// make some global injections -global.requestAnimationFrame = (cb) => { - setImmediate(cb); -}; - -// create canvas in Node env -Konva.Util.createCanvasElement = () => { - const node = new canvas.Canvas(); - node.style = {}; - return node; -}; - -// create image in Node env -Konva.Util.createImageElement = () => { - const node = new canvas.Image(); - node.style = {}; - return node; -}; - -// _checkVisibility use dom element, in node we can skip it -Konva.Stage.prototype._checkVisibility = () => {}; - -module.exports = Konva; diff --git a/konva-node/package.json b/konva-node/package.json deleted file mode 100644 index df2a8ad5..00000000 --- a/konva-node/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "konva-node", - "version": "0.11.2", - "description": "Konva framework for NodeJS env", - "main": "index.js", - "files": [ - "index.js" - ], - "type": "module", - "typings": "./node_modules/konva/konva.d.ts", - "scripts": {}, - "keywords": [ - "canvas", - "animations", - "graphic", - "html5" - ], - "author": "Anton Lavrenov", - "bugs": { - "url": "https://github.com/konvajs/konva/issues" - }, - "homepage": "http://konvajs.org/", - "repository": { - "type": "git", - "url": "git://github.com/konvajs/konva.git" - }, - "license": "MIT", - "dependencies": { - "canvas": "^2.5.0", - "konva": "^7" - } -} diff --git a/package.json b/package.json index d34289cf..ec3499c7 100644 --- a/package.json +++ b/package.json @@ -3,16 +3,23 @@ "version": "9.3.22", "description": "HTML5 2d canvas library.", "author": "Anton Lavrenov", + "type": "module", "files": [ "README.md", "konva.js", "konva.min.js", - "lib", - "cmj" + "lib" ], - "main": "./lib/index-node.js", - "browser": "./lib/index.js", - "typings": "./lib/index-types.d.ts", + "exports": { + ".": { + "default": "./lib/index.js" + }, + "./setup-node-canvas": { + "default": "./lib/setup-node-canvas.js" + } + }, + "main": "./lib/index.js", + "types": "./lib/index-types.d.ts", "scripts": { "start": "npm run test:watch", "compile": "npm run clean && npm run tsc && cp ./src/index-types.d.ts ./lib/index-types.d.ts && npm run rollup", @@ -22,10 +29,12 @@ "test:build": "PARCEL_WORKER_BACKEND=process parcel build ./test/unit-tests.html --dist-dir ./test-build --target none --public-url ./ --no-source-maps", "test:browser": "npm run test:build && mocha-headless-chrome -f ./test-build/unit-tests.html -a disable-web-security -a no-sandbox -a disable-setuid-sandbox", "test:watch": "rm -rf ./.parcel-cache && PARCEL_WORKERS=0 parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html ./test/bunnies.html", - "test:node:compiled": "rm -rf ./.test-temp && mkdir ./.test-temp && (tsc -p ./test/tsconfig.json --outDir ./.test-temp || true) && mocha './.test-temp/test/unit/**/*.js' -r ./test/node-global-setup.mjs --exit && rm -rf ./.test-temp && npm run test:import", - "test:node": "npm run test:node:compiled", + + "test:node:canvas": "rm -rf ./.test-temp && mkdir ./.test-temp && (tsc -p ./test/tsconfig.json --outDir ./.test-temp || true) && node ./rename-imports-test.mjs && mocha './.test-temp/test/unit/**/*.js' -r ./test/node-canvas-global-setup.mjs --exit && rm -rf ./.test-temp && npm run test:import", + "test:node:skia": "rm -rf ./.test-temp && mkdir ./.test-temp && (tsc -p ./test/tsconfig.json --outDir ./.test-temp || true) && node ./rename-imports-test.mjs && mocha './.test-temp/test/unit/**/*.js' -r ./test/node-skia-global-setup.mjs --exit && rm -rf ./.test-temp && npm run test:import", + "test:node": "npm run test:node:canvas && npm run test:node:skia", "tsc": "tsc --removeComments", - "rollup": "rollup -c --bundleConfigAsCjs", + "rollup": "rollup -c", "clean": "rm -rf ./lib && rm -rf ./types && rm -rf ./cmj && rm -rf ./test-build", "watch": "rollup -c -w", "size": "size-limit" @@ -61,18 +70,18 @@ } ], "devDependencies": { - "@parcel/transformer-image": "2.13.2", - "@size-limit/preset-big-lib": "^11.1.6", + "@parcel/transformer-image": "2.15.4", + "@size-limit/preset-big-lib": "^11.2.0", "@types/mocha": "^10.0.10", - "canvas": "^3.1.0", - "chai": "5.1.2", + "canvas": "^3.1.2", + "chai": "5.2.1", "filehound": "^1.17.6", - "gulp": "^5.0.0", + "gulp": "^5.0.1", "gulp-concat": "^2.6.1", "gulp-connect": "^5.7.0", "gulp-exec": "^5.0.0", "gulp-jsdoc3": "^3.0.0", - "gulp-rename": "^2.0.0", + "gulp-rename": "^2.1.0", "gulp-replace": "^1.1.4", "gulp-typescript": "^5.0.1", "gulp-uglify": "^3.0.2", @@ -82,12 +91,13 @@ "mocha-headless-chrome": "^4.0.0", "parcel": "2.13.3", "process": "^0.11.10", - "rollup": "^4.31.0", + "rollup": "^4.46.2", "rollup-plugin-typescript2": "^0.36.0", - "size-limit": "^11.1.6", - "ts-mocha": "^10.0.0", + "size-limit": "^11.2.0", + "skia-canvas": "^2.0.2", + "ts-mocha": "^11.1.0", "ts-node": "^10.9.2", - "typescript": "^5.7.3" + "typescript": "^5.9.2" }, "keywords": [ "canvas", diff --git a/rename-imports-test.mjs b/rename-imports-test.mjs new file mode 100644 index 00000000..4b2feb92 --- /dev/null +++ b/rename-imports-test.mjs @@ -0,0 +1,36 @@ +import FileHound from 'filehound'; +import fs from 'fs'; + +const files = FileHound.create().paths('./.test-temp').ext(['js', 'ts']).find(); + +files.then((filePaths) => { + filePaths.forEach((filepath) => { + fs.readFile(filepath, 'utf8', (err, text) => { + if (!text.match(/import .* from/g)) { + return; + } + text = text.replace(/(import .* from\s+['"])(.*)(?=['"])/g, '$1$2.js'); + if (text.match(/export .* from/g)) { + text = text.replace(/(export .* from\s+['"])(.*)(?=['"])/g, '$1$2.js'); + } + + if (err) throw err; + + // stupid replacement back + text = text.replace("from 'canvas.js';", "from 'canvas';"); + text = text.replace("from 'chai.js';", "from 'chai';"); + text = text.replace("from 'skia-canvas.js';", "from 'skia-canvas';"); + + // Handle import("./x/y/z") syntax. + text = text.replace(/(import\s*\(\s*['"])(.*)(?=['"])/g, '$1$2.js'); + + fs.writeFile(filepath, text, function (err) { + if (err) { + throw err; + } + }); + }); + }); +}); + +// Removed CommonJS export rewriting to keep ESM output intact diff --git a/rename-imports.mjs b/rename-imports.mjs index db66051d..3b1ba401 100644 --- a/rename-imports.mjs +++ b/rename-imports.mjs @@ -34,14 +34,4 @@ files.then((filePaths) => { }); }); -const indexFiles = ['lib/index.js', 'lib/index-node.js', 'lib/Core.js']; -indexFiles.forEach((filepath) => { - fs.readFile(filepath, 'utf8', (err, text) => { - text = text.replace('exports.default =', 'module.exports ='); - fs.writeFile(filepath, text, function (err) { - if (err) { - throw err; - } - }); - }); -}); +// Removed CommonJS export rewriting to keep ESM output intact diff --git a/rollup.config.mjs b/rollup.config.mjs index 8d3f7f5c..4af2c9c4 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -11,16 +11,12 @@ export default { sourcemap: false, freeze: false, }, - // { file: pkg.module, format: 'es', sourcemap: true } ], - // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash') external: [], watch: { include: 'src/**', }, plugins: [ - // Allow json resolution - // json(), // Compile TypeScript files typescript({ useTsconfigDeclarationDir: true, @@ -32,14 +28,5 @@ export default { }, }, }), - // // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs) - // commonjs(), - // // Allow node_modules resolution, so you can use 'external' to control - // // which external modules to include in the bundle - // // https://github.com/rollup/rollup-plugin-node-resolve#usage - // resolve(), - - // Resolve source maps to the original source - // sourceMaps() ], }; diff --git a/src/Global.ts b/src/Global.ts index 9996e398..c8c08bb6 100644 --- a/src/Global.ts +++ b/src/Global.ts @@ -184,6 +184,11 @@ export const Konva = { // insert Konva into global namespace (window) // it is required for npm packages _injectGlobal(Konva) { + if (typeof glob.Konva !== 'undefined') { + console.error( + 'Severa Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.' + ); + } glob.Konva = Konva; }, }; diff --git a/src/Util.ts b/src/Util.ts index ae5075a5..890a7d8f 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -2,6 +2,14 @@ import { Konva } from './Global'; import { Context } from './Context'; import { IRect, RGB, Vector2d } from './types'; +const ensureBrowser = () => { + if (typeof document === 'undefined') { + throw new Error( + 'document is undefined, this is not a browser environment. For node.js env add `import "konva/register-node"`' + ); + } +}; + /* * Last updated November 2011 * By Simon Sarris @@ -424,7 +432,7 @@ const OBJECT_ARRAY = '[object Array]', yellowgreen: [154, 205, 5], }, RGB_REGEX = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/; - let animQueue: Array = []; +let animQueue: Array = []; const req = (typeof requestAnimationFrame !== 'undefined' && requestAnimationFrame) || @@ -505,6 +513,7 @@ export const Util = { } }, createCanvasElement() { + ensureBrowser(); const canvas = document.createElement('canvas'); // on some environments canvas.style is readonly try { @@ -513,6 +522,7 @@ export const Util = { return canvas; }, createImageElement() { + ensureBrowser(); return document.createElement('img'); }, _isInDocument(el: any) { diff --git a/src/setup-node-canvas.ts b/src/setup-node-canvas.ts new file mode 100644 index 00000000..ff12a4ac --- /dev/null +++ b/src/setup-node-canvas.ts @@ -0,0 +1,30 @@ +import { Konva } from './_FullInternals'; +import * as Canvas from 'canvas'; + +const canvas = Canvas['default'] || Canvas; + +global.DOMMatrix = canvas.DOMMatrix; + +const isNode = typeof global.document === 'undefined'; + +if (isNode) { + Konva.Util['createCanvasElement'] = () => { + const node = canvas.createCanvas(300, 300) as any; + if (!node['style']) { + node['style'] = {}; + } + return node; + }; + + // create image in Node env + Konva.Util.createImageElement = () => { + const node = new canvas.Image() as any; + return node; + }; + + // this line is not part of the public API + // but will be used in tests + Konva.Util['Canvas'] = Canvas; +} + +export default Konva; diff --git a/src/setup-node-skia.ts b/src/setup-node-skia.ts new file mode 100644 index 00000000..cf3379da --- /dev/null +++ b/src/setup-node-skia.ts @@ -0,0 +1,40 @@ +import { Konva } from './_FullInternals'; +import { Canvas, DOMMatrix, Image } from 'skia-canvas'; + +global.DOMMatrix = DOMMatrix as any; + +const isNode = typeof global.document === 'undefined'; + +if (isNode) { + // @ts-ignore + Canvas.prototype.toDataURL = Canvas.prototype.toDataURLSync; + + Konva.Util['createCanvasElement'] = () => { + const node = new Canvas(300, 300) as any; + if (!node['style']) { + node['style'] = {}; + } + node.toString = () => '[object HTMLCanvasElement]'; + const ctx = node.getContext('2d'); + // Override the getter to return the canvas node directly + // because in skia-canvas canvas is using weak ref to the canvas node + // and somehow on many tests it fails to get the canvas node + Object.defineProperty(ctx, 'canvas', { + get: () => node, + }); + return node; + }; + + // create image in Node env + Konva.Util.createImageElement = () => { + const node = new Image() as any; + node.toString = () => '[object HTMLImageElement]'; + return node; + }; + + // this line is not part of the public API + // but will be used in tests + Konva.Util['isSkia'] = true; +} + +export default Konva; diff --git a/test/import-test.cjs b/test/import-test.cjs index c060a3cd..2c7b8359 100644 --- a/test/import-test.cjs +++ b/test/import-test.cjs @@ -1,5 +1,6 @@ // try to import only core -const Konva = require('../'); +const Konva = require('../').default; +require('../lib/setup-node-canvas'); // just do a simple action const stage = new Konva.Stage(); diff --git a/test/import-test.mjs b/test/import-test.mjs index cebddda1..54d644a4 100644 --- a/test/import-test.mjs +++ b/test/import-test.mjs @@ -4,15 +4,15 @@ function equal(val1, val2, message) { } } -// try to import only core +// try to import only core from built lib import Konva from '../lib/Core.js'; +import '../lib/setup-node-canvas.js'; import { Rect } from '../lib/shapes/Rect.js'; -import '../lib/index-node.js'; equal(Rect !== undefined, true, 'Rect is defined'); equal(Konva.Rect, Rect, 'Rect is injected'); -// // just do a simple action +// just do a simple action const stage = new Konva.Stage(); stage.toDataURL(); diff --git a/test/manual/Manual-test.ts b/test/manual/Manual-test.ts index 1a506a04..32bf3294 100644 --- a/test/manual/Manual-test.ts +++ b/test/manual/Manual-test.ts @@ -1,6 +1,6 @@ import { assert } from 'chai'; -import { addStage, Konva, loadImage } from '../unit/test-utils'; +import { addStage, Konva, loadImage, showHit } from '../unit/test-utils'; describe('Manual', function () { // ====================================================== diff --git a/test/node-canvas-global-setup.mjs b/test/node-canvas-global-setup.mjs new file mode 100644 index 00000000..1feda238 --- /dev/null +++ b/test/node-canvas-global-setup.mjs @@ -0,0 +1,21 @@ +export async function mochaGlobalSetup() { + // Load node-canvas polyfills on the compiled test output + // Path from this file (test/) to compiled file (.test-temp/src/...) + try { + await import( + new URL('../.test-temp/src/setup-node-canvas.js', import.meta.url) + ); + } catch (e) { + // If not compiled yet or path missing, keep going; tests that need it will fail clearly + } + + globalThis.Path2D ??= class Path2D { + constructor(path) { + this.path = path; + } + + get [Symbol.toStringTag]() { + return `Path2D`; + } + }; +} diff --git a/test/node-global-setup.mjs b/test/node-global-setup.mjs deleted file mode 100644 index b54de724..00000000 --- a/test/node-global-setup.mjs +++ /dev/null @@ -1,11 +0,0 @@ -export function mochaGlobalSetup() { - globalThis.Path2D ??= class Path2D { - constructor(path) { - this.path = path - } - - get [Symbol.toStringTag]() { - return `Path2D`; - } - } -} diff --git a/test/node-skia-global-setup.mjs b/test/node-skia-global-setup.mjs new file mode 100644 index 00000000..21da463c --- /dev/null +++ b/test/node-skia-global-setup.mjs @@ -0,0 +1,15 @@ +export async function mochaGlobalSetup() { + await import( + new URL('../.test-temp/src/setup-node-skia.js', import.meta.url) + ); + + globalThis.Path2D ??= class Path2D { + constructor(path) { + this.path = path; + } + + get [Symbol.toStringTag]() { + return `Path2D`; + } + }; +} diff --git a/test/tsconfig.json b/test/tsconfig.json index 1ab79b25..5556fbdd 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -4,7 +4,7 @@ "noEmitOnError": false, "moduleResolution": "node", "lib": ["ES2015", "dom"], - "module": "CommonJS", + "module": "ESNext", "skipLibCheck": true, "noImplicitAny": false, "allowJs": true, diff --git a/test/unit/Group-test.ts b/test/unit/Group-test.ts index c2ec769f..14c69ad8 100644 --- a/test/unit/Group-test.ts +++ b/test/unit/Group-test.ts @@ -86,8 +86,6 @@ describe('Group', function () { const trace = layer.getContext().getTrace(); - console.log(trace); - assert.equal( trace, 'clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);beginPath();rect(0,0,0,0);clip();transform(1,0,0,1,0,0);restore();' diff --git a/test/unit/Image-test.ts b/test/unit/Image-test.ts index 34ac6d87..834b048c 100644 --- a/test/unit/Image-test.ts +++ b/test/unit/Image-test.ts @@ -327,7 +327,7 @@ describe('Image', function () { layer.add(image); layer.draw(); assert.equal(image instanceof Konva.Image, true); - var nativeImg = image.image(); + var nativeImg = image.image() as HTMLImageElement; assert.equal(nativeImg instanceof Image, true); assert.equal(nativeImg.src.indexOf(src) !== -1, true); assert.equal(nativeImg.complete, true); diff --git a/test/unit/Label-test.ts b/test/unit/Label-test.ts index 1f3e3162..c967c24e 100644 --- a/test/unit/Label-test.ts +++ b/test/unit/Label-test.ts @@ -366,7 +366,7 @@ describe('Label', function () { layer.add(label); assert.equal(counter, 4); - tag.pointerDirection('bottom'); + tag.pointerDirection('down'); assert.equal(counter, 5); tag.pointerWidth(30); assert.equal(counter, 6); diff --git a/test/unit/MouseEvents-test.ts b/test/unit/MouseEvents-test.ts index b2d010f2..cb369f3d 100644 --- a/test/unit/MouseEvents-test.ts +++ b/test/unit/MouseEvents-test.ts @@ -2068,7 +2068,7 @@ describe('MouseEvents', function () { type: 'mouseenter', }; - stage._pointerenter(evt); + stage._pointerenter(evt as PointerEvent); assert.equal(mouseenterCount, 1, 'mouseenterCount should be 1'); }); @@ -2268,7 +2268,7 @@ describe('MouseEvents', function () { type: 'mouseenter', }; - stage._pointerenter(evt); + stage._pointerenter(evt as PointerEvent); simulateMouseMove(stage, { x: 10, diff --git a/test/unit/Node-test.ts b/test/unit/Node-test.ts index b43268db..8e3359c4 100644 --- a/test/unit/Node-test.ts +++ b/test/unit/Node-test.ts @@ -2552,11 +2552,18 @@ describe('Node', function () { }); it('make sure we can create non existing node type', function () { + const oldWarn = console.warn; + let called = false; + console.warn = function () { + called = true; + }; var json = '{"attrs":{},"className":"Layer","children":[{"attrs":{},"className":"Group","children":[{"attrs":{"x":289,"y":100,"radius":70,"fill":"green","stroke":"black","strokeWidth":4,"name":"myCircle","draggable":true},"className":"WeirdShape"}]}]}'; var layer = Konva.Node.create(json); assert.deepEqual(layer.find('Shape').length, 1); + console.warn = oldWarn; + assert.equal(called, true); }); // ====================================================== @@ -3478,6 +3485,7 @@ describe('Node', function () { assert.equal(rect.findAncestor('#group'), group); assert.equal(rect.findAncestor('Group'), group); + // @ts-expect-error - test for no selector assert.equal(rect.findAncestor(), null, 'return null if no selector'); }); diff --git a/test/unit/Shape-test.ts b/test/unit/Shape-test.ts index cb866073..49d9299a 100644 --- a/test/unit/Shape-test.ts +++ b/test/unit/Shape-test.ts @@ -1160,6 +1160,8 @@ describe('Shape', function () { // no we should hit the rect assert.equal(stage.getIntersection({ x: 5, y: 5 }), rect); + const oldWarn = console.warn; + console.warn = function () {}; rect.strokeHitEnabled(false); assert.equal(rect.hitStrokeWidth(), 0); @@ -1171,15 +1173,7 @@ describe('Shape', function () { rect.hitStrokeWidth(0); assert.equal(rect.strokeHitEnabled(), false); - - // var trace = layer - // .getHitCanvas() - // .getContext() - // .getTrace(true); - // assert.equal( - // trace, - // 'clearRect();save();transform();beginPath();rect();closePath();save();fillStyle;fill();restore();restore();' - // ); + console.warn = oldWarn; }); it('enable hitStrokeWidth even if we have no stroke on scene', function () { diff --git a/test/unit/Stage-test.ts b/test/unit/Stage-test.ts index 48ab5ce7..dff6d61f 100644 --- a/test/unit/Stage-test.ts +++ b/test/unit/Stage-test.ts @@ -551,6 +551,11 @@ describe('Stage', function () { // ====================================================== it('Should not throw on clip for stage', function () { // no asserts, because we check throw + const oldWarn = console.warn; + let called = false; + console.warn = function () { + called = true; + }; var stage = addStage({ clipFunc: function () {}, }); @@ -566,6 +571,8 @@ describe('Stage', function () { layer.add(text); stage.add(layer); + console.warn = oldWarn; + assert.equal(called, true); }); // ====================================================== diff --git a/test/unit/Text-test.ts b/test/unit/Text-test.ts index e66ef825..22765b5b 100644 --- a/test/unit/Text-test.ts +++ b/test/unit/Text-test.ts @@ -1780,10 +1780,15 @@ describe('Text', function () { layer.draw(); Konva._fixTextRendering = false; - const trace = - 'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);font=normal normal 100px Arial;textBaseline=alphabetic;textAlign=left;translate(0,0);save();fillStyle=black;fillText(hello,0,85);restore();restore();'; - - assert.equal(layer.getContext().getTrace(), trace); + if (Konva.Util['isSkia']) { + const trace = + 'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);font=normal normal 100px Arial;textBaseline=alphabetic;textAlign=left;translate(0,0);save();fillStyle=black;fillText(hello,0,84.668);restore();restore();'; + assert.equal(layer.getContext().getTrace(), trace); + } else { + const trace = + 'clearRect(0,0,578,200);clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);font=normal normal 100px Arial;textBaseline=alphabetic;textAlign=left;translate(0,0);save();fillStyle=black;fillText(hello,0,85);restore();restore();'; + assert.equal(layer.getContext().getTrace(), trace); + } }); it('charRenderFunc draws per character and can mutate context', function () { diff --git a/test/unit/TextPath-test.ts b/test/unit/TextPath-test.ts index e03a4095..f492c008 100644 --- a/test/unit/TextPath-test.ts +++ b/test/unit/TextPath-test.ts @@ -877,7 +877,7 @@ describe('TextPath', function () { var rect = textpath.getClientRect(); assert.equal(Math.round(rect.width), 299); - assert.equal(Math.round(rect.height), 171); + assert.equal(Math.abs(Math.round(rect.height) - 171) < 2, true); }); it.skip('check vertical text path', function () { diff --git a/test/unit/Transformer-test.ts b/test/unit/Transformer-test.ts index 1c930314..14212a02 100644 --- a/test/unit/Transformer-test.ts +++ b/test/unit/Transformer-test.ts @@ -1,5 +1,6 @@ import { assert } from 'chai'; import { Transformer } from '../../src/shapes/Transformer'; +import type { Rect } from '../../src/shapes/Rect'; import { addStage, @@ -4842,8 +4843,15 @@ describe('Transformer', function () { const tr = new Konva.Transformer(); layer.add(tr); + const oldError = console.error; + let called = false; + console.error = function () { + called = true; + }; tr.nodes([layer]); assert.equal(tr.nodes().length, 0); + console.error = oldError; + assert.equal(called, true); }); it('anchorStyleFunc', function () { @@ -4866,7 +4874,7 @@ describe('Transformer', function () { }); layer.add(tr); // manual check of correct position of node - var handler = tr.findOne('.bottom-right'); + var handler = tr.findOne('.bottom-right'); assert.equal(handler.fill(), 'white'); tr.anchorStyleFunc((anchor) => { diff --git a/test/unit/imagediff.ts b/test/unit/imagediff.ts index 17bc1423..f0895ea9 100644 --- a/test/unit/imagediff.ts +++ b/test/unit/imagediff.ts @@ -1,4 +1,5 @@ -import { createCanvas, Canvas } from 'canvas'; +import KonvaModule from '../../src/index'; +export const Konva = KonvaModule; var TYPE_ARRAY = /\[object Array\]/i, TYPE_CANVAS = /\[object (Canvas|HTMLCanvasElement)\]/i, @@ -6,15 +7,24 @@ var TYPE_ARRAY = /\[object Array\]/i, TYPE_CONTEXT = /\[object CanvasRenderingContext2D\]/i, TYPE_IMAGE = /\[object (Image|HTMLImageElement)\]/i, TYPE_IMAGE_DATA = /\[object ImageData\]/i, - UNDEFINED = 'undefined', - canvas = getCanvas(), - context = canvas.getContext('2d'); + UNDEFINED = 'undefined'; // Creation function getCanvas(width?, height?) { - return createCanvas(width, height); + return Konva.Util.createCanvasElement(); } + +let singleCanvas; +function getSingleCanvas() { + if (!singleCanvas) { + singleCanvas = getCanvas(); + } + return singleCanvas; +} + function getImageData(width, height) { + const canvas = getSingleCanvas(); + const context = canvas.getContext('2d'); canvas.width = width; canvas.height = height; context.clearRect(0, 0, width, height); @@ -26,7 +36,7 @@ function isImage(object) { return isType(object, TYPE_IMAGE); } function isCanvas(object) { - return isType(object, TYPE_CANVAS) || object instanceof Canvas; + return isType(object, TYPE_CANVAS); } function isContext(object) { return isType(object, TYPE_CONTEXT); @@ -49,10 +59,7 @@ function isImageType(object) { ); } function isType(object, type) { - return ( - typeof object === 'object' && - !!Object.prototype.toString.apply(object).match(type) - ); + return typeof object === 'object' && !!object.toString().match(type); } // Type Conversion @@ -61,6 +68,8 @@ function copyImageData(imageData) { width = imageData.width, data = imageData.data; + const canvas = getSingleCanvas(); + const context = canvas.getContext('2d'); canvas.width = width; canvas.height = height; const newImageData = context.getImageData(0, 0, width, height); @@ -89,6 +98,8 @@ function toImageData(object) { function toImageDataFromImage(image) { const height = image.height, width = image.width; + const canvas = getSingleCanvas(); + const context = canvas.getContext('2d'); canvas.width = width; canvas.height = height; context.clearRect(0, 0, width, height); @@ -189,8 +200,7 @@ function diffUnequal(a, b, options) { bData = b.data, cData = c.data, align = options && options.align; - var rowOffset, - columnOffset; + var rowOffset, columnOffset; for (let i = cData.length - 1; i > 0; i = i - 4) { cData[i] = 255; @@ -239,10 +249,10 @@ function diffUnequal(a, b, options) { function checkType(...args) { for (let i = 0; i < args.length; i++) { if (!isImageType(args[i])) { - throw { - name: 'ImageTypeError', - message: 'Submitted object was not an image.', - }; + // throw { + // name: 'ImageTypeError', + // message: 'Submitted object was not an image.', + // }; } } } diff --git a/test/unit/test-utils.ts b/test/unit/test-utils.ts index 12e4df84..8b595463 100644 --- a/test/unit/test-utils.ts +++ b/test/unit/test-utils.ts @@ -1,10 +1,9 @@ import { assert } from 'chai'; import KonvaModule from '../../src/index'; -import '../../src/index-node'; export const Konva = KonvaModule; -import * as canvas from 'canvas'; +// import * as canvas from 'canvas'; Konva.enableTrace = true; Konva.showWarnings = true; @@ -84,12 +83,14 @@ export function loadImage(url, callback) { url = (document.getElementById(url) as HTMLImageElement).src; } - return canvas - .loadImage(url) - .then(callback) - .catch((e) => { - console.error(e); - }); + const image = Konva.Util.createImageElement(); + image.onload = () => { + callback(image); + }; + image.onerror = (e) => { + console.error('Error loading image', url, e); + }; + image.src = url; } export function getPixelRatio() { @@ -171,7 +172,7 @@ export function compareLayers(layer1: Layer, layer2: Layer, tol?, secondTol?) { } export function createCanvas() { - var node = canvas.createCanvas(300, 300); + var node = Konva.Util.createCanvasElement(); node.width = 578 * Konva.pixelRatio; node.height = 200 * Konva.pixelRatio; node.getContext('2d').scale(Konva.pixelRatio, Konva.pixelRatio);