mirror of
https://github.com/penpot/penpot.git
synced 2025-05-08 02:35:53 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
6f5c7c01bb
61 changed files with 583 additions and 506 deletions
|
@ -524,6 +524,19 @@
|
||||||
(when (kbd/enter? event)
|
(when (kbd/enter? event)
|
||||||
(on-add-shared event))))
|
(on-add-shared event))))
|
||||||
|
|
||||||
|
on-show-version-history
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps file-id)
|
||||||
|
(fn [_]
|
||||||
|
(st/emit! (dw/toggle-layout-flag :document-history))))
|
||||||
|
|
||||||
|
on-show-version-history-key-down
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps on-show-version-history)
|
||||||
|
(fn [event]
|
||||||
|
(when (kbd/enter? event)
|
||||||
|
(on-show-version-history event))))
|
||||||
|
|
||||||
on-export-shapes
|
on-export-shapes
|
||||||
(mf/use-fn #(st/emit! (de/show-workspace-export-dialog {:origin "workspace:menu"})))
|
(mf/use-fn #(st/emit! (de/show-workspace-export-dialog {:origin "workspace:menu"})))
|
||||||
|
|
||||||
|
@ -575,14 +588,23 @@
|
||||||
:on-click on-remove-shared
|
:on-click on-remove-shared
|
||||||
:on-key-down on-remove-shared-key-down
|
:on-key-down on-remove-shared-key-down
|
||||||
:id "file-menu-remove-shared"}
|
:id "file-menu-remove-shared"}
|
||||||
[:span {:class (stl/css :item-name)} (tr "dashboard.unpublish-shared")]])
|
[:span {:class (stl/css :item-name)}
|
||||||
|
(tr "dashboard.unpublish-shared")]])
|
||||||
|
|
||||||
(when can-edit
|
(when can-edit
|
||||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||||
:on-click on-add-shared
|
:on-click on-add-shared
|
||||||
:on-key-down on-add-shared-key-down
|
:on-key-down on-add-shared-key-down
|
||||||
:id "file-menu-add-shared"}
|
:id "file-menu-add-shared"}
|
||||||
[:span {:class (stl/css :item-name)} (tr "dashboard.add-shared")]]))
|
[:span {:class (stl/css :item-name)}
|
||||||
|
(tr "dashboard.add-shared")]]))
|
||||||
|
|
||||||
|
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||||
|
:on-click on-show-version-history
|
||||||
|
:on-key-down on-show-version-history-key-down
|
||||||
|
:id "file-menu-show-version-history"}
|
||||||
|
[:span {:class (stl/css :item-name)}
|
||||||
|
(tr "dashboard.show-version-history")]]
|
||||||
|
|
||||||
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
|
||||||
:on-click on-export-shapes
|
:on-click on-export-shapes
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { describe, test, expect, vi } from 'vitest';
|
|
||||||
import { addEventListeners, removeEventListeners } from './Event';
|
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
|
||||||
describe('Event', () => {
|
|
||||||
test('addEventListeners should add event listeners to an element using an object', () => {
|
|
||||||
const clickSpy = vi.fn();
|
|
||||||
const events = {
|
|
||||||
click: clickSpy
|
|
||||||
}
|
|
||||||
const element = document.createElement('div');
|
|
||||||
addEventListeners(element, events);
|
|
||||||
element.dispatchEvent(new Event('click'));
|
|
||||||
expect(clickSpy).toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('removeEventListeners should remove event listeners to an element using an object', () => {
|
|
||||||
const clickSpy = vi.fn();
|
|
||||||
const events = {
|
|
||||||
click: clickSpy,
|
|
||||||
};
|
|
||||||
const element = document.createElement("div");
|
|
||||||
addEventListeners(element, events);
|
|
||||||
element.dispatchEvent(new Event("click"));
|
|
||||||
removeEventListeners(element, events);
|
|
||||||
element.dispatchEvent(new Event('click'))
|
|
||||||
expect(clickSpy).toBeCalledTimes(1);
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"paths": {
|
"paths": {
|
||||||
"~/*": ["./*"]
|
"~/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,9 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "editor/TextEditor.js",
|
"main": "src/editor/TextEditor.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "./scripts/build.sh",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"coverage": "vitest run --coverage",
|
"coverage": "vitest run --coverage",
|
||||||
"test": "vitest --run",
|
"test": "vitest --run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
|
@ -23,6 +21,7 @@
|
||||||
"esbuild": "^0.24.0",
|
"esbuild": "^0.24.0",
|
||||||
"jsdom": "^25.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"playwright": "^1.45.1",
|
"playwright": "^1.45.1",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
"vite": "^5.3.1",
|
"vite": "^5.3.1",
|
||||||
"vitest": "^1.6.0"
|
"vitest": "^1.6.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
esbuild --bundle --minify --sourcemap --target=es2021 --format=esm --platform=browser editor/TextEditor.js --outfile=dist/TextEditor.mjs
|
|
29
frontend/text-editor/src/editor/Event.test.js
Normal file
29
frontend/text-editor/src/editor/Event.test.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { describe, test, expect, vi } from "vitest";
|
||||||
|
import { addEventListeners, removeEventListeners } from "./Event.js";
|
||||||
|
|
||||||
|
/* @vitest-environment jsdom */
|
||||||
|
describe("Event", () => {
|
||||||
|
test("addEventListeners should add event listeners to an element using an object", () => {
|
||||||
|
const clickSpy = vi.fn();
|
||||||
|
const events = {
|
||||||
|
click: clickSpy,
|
||||||
|
};
|
||||||
|
const element = document.createElement("div");
|
||||||
|
addEventListeners(element, events);
|
||||||
|
element.dispatchEvent(new Event("click"));
|
||||||
|
expect(clickSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("removeEventListeners should remove event listeners to an element using an object", () => {
|
||||||
|
const clickSpy = vi.fn();
|
||||||
|
const events = {
|
||||||
|
click: clickSpy,
|
||||||
|
};
|
||||||
|
const element = document.createElement("div");
|
||||||
|
addEventListeners(element, events);
|
||||||
|
element.dispatchEvent(new Event("click"));
|
||||||
|
removeEventListeners(element, events);
|
||||||
|
element.dispatchEvent(new Event("click"));
|
||||||
|
expect(clickSpy).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -8,15 +8,15 @@
|
||||||
|
|
||||||
import clipboard from "./clipboard/index.js";
|
import clipboard from "./clipboard/index.js";
|
||||||
import commands from "./commands/index.js";
|
import commands from "./commands/index.js";
|
||||||
import ChangeController from './controllers/ChangeController.js';
|
import ChangeController from "./controllers/ChangeController.js";
|
||||||
import SelectionController from './controllers/SelectionController.js';
|
import SelectionController from "./controllers/SelectionController.js";
|
||||||
import { createSelectionImposterFromClientRects } from './selection/Imposter.js';
|
import { createSelectionImposterFromClientRects } from "./selection/Imposter.js";
|
||||||
import { addEventListeners, removeEventListeners } from "./Event.js";
|
import { addEventListeners, removeEventListeners } from "./Event.js";
|
||||||
import { createRoot, createEmptyRoot } from './content/dom/Root.js';
|
import { createRoot, createEmptyRoot } from "./content/dom/Root.js";
|
||||||
import { createParagraph, fixParagraph, getParagraph } from './content/dom/Paragraph.js';
|
import { createParagraph } from "./content/dom/Paragraph.js";
|
||||||
import { createEmptyInline, createInline } from './content/dom/Inline.js';
|
import { createEmptyInline, createInline } from "./content/dom/Inline.js";
|
||||||
import { isLineBreak } from './content/dom/LineBreak.js';
|
import { isLineBreak } from "./content/dom/LineBreak.js";
|
||||||
import LayoutType from './layout/LayoutType.js';
|
import LayoutType from "./layout/LayoutType.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text Editor.
|
* Text Editor.
|
|
@ -1,49 +1,49 @@
|
||||||
import { describe, test, expect } from 'vitest'
|
import { describe, test, expect } from "vitest";
|
||||||
import { TextEditor } from './TextEditor'
|
import { TextEditor } from "./TextEditor.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe('TextEditor', () => {
|
describe("TextEditor", () => {
|
||||||
test('Creating TextEditor without element should throw', () => {
|
test("Creating TextEditor without element should throw", () => {
|
||||||
expect(() => new TextEditor()).toThrowError('Invalid text editor element');
|
expect(() => new TextEditor()).toThrowError("Invalid text editor element");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Creating TextEditor with element should success', () => {
|
test("Creating TextEditor with element should success", () => {
|
||||||
expect(new TextEditor(document.createElement('div'))).toBeInstanceOf(TextEditor);
|
expect(new TextEditor(document.createElement("div"))).toBeInstanceOf(
|
||||||
|
TextEditor,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('isEmpty should return true when editor is empty', () => {
|
test("isEmpty should return true when editor is empty", () => {
|
||||||
const textEditor = new TextEditor(document.createElement("div"));
|
const textEditor = new TextEditor(document.createElement("div"));
|
||||||
expect(textEditor).toBeInstanceOf(TextEditor);
|
expect(textEditor).toBeInstanceOf(TextEditor);
|
||||||
expect(textEditor.isEmpty).toBe(true);
|
expect(textEditor.isEmpty).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Num paragraphs should return 1 when empty', () => {
|
test("Num paragraphs should return 1 when empty", () => {
|
||||||
const textEditor = new TextEditor(document.createElement("div"));
|
const textEditor = new TextEditor(document.createElement("div"));
|
||||||
expect(textEditor).toBeInstanceOf(TextEditor);
|
expect(textEditor).toBeInstanceOf(TextEditor);
|
||||||
expect(textEditor.numParagraphs).toBe(1);
|
expect(textEditor.numParagraphs).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Num paragraphs should return the number of paragraphs', () => {
|
test("Num paragraphs should return the number of paragraphs", () => {
|
||||||
const textEditor = new TextEditor(document.createElement("div"));
|
const textEditor = new TextEditor(document.createElement("div"));
|
||||||
textEditor.root = textEditor.createRoot([
|
textEditor.root = textEditor.createRoot([
|
||||||
textEditor.createParagraph([
|
textEditor.createParagraph([
|
||||||
textEditor.createInlineFromString('Hello, World!')
|
textEditor.createInlineFromString("Hello, World!"),
|
||||||
|
]),
|
||||||
|
textEditor.createParagraph([textEditor.createInlineFromString("")]),
|
||||||
|
textEditor.createParagraph([
|
||||||
|
textEditor.createInlineFromString("¡Hola, Mundo!"),
|
||||||
]),
|
]),
|
||||||
textEditor.createParagraph([
|
textEditor.createParagraph([
|
||||||
textEditor.createInlineFromString('')
|
textEditor.createInlineFromString("Hallo, Welt!"),
|
||||||
]),
|
]),
|
||||||
textEditor.createParagraph([
|
|
||||||
textEditor.createInlineFromString('¡Hola, Mundo!')
|
|
||||||
]),
|
|
||||||
textEditor.createParagraph([
|
|
||||||
textEditor.createInlineFromString('Hallo, Welt!')
|
|
||||||
])
|
|
||||||
]);
|
]);
|
||||||
expect(textEditor).toBeInstanceOf(TextEditor);
|
expect(textEditor).toBeInstanceOf(TextEditor);
|
||||||
expect(textEditor.numParagraphs).toBe(4);
|
expect(textEditor.numParagraphs).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Disposing a TextEditor nullifies everything', () => {
|
test("Disposing a TextEditor nullifies everything", () => {
|
||||||
const textEditor = new TextEditor(document.createElement("div"));
|
const textEditor = new TextEditor(document.createElement("div"));
|
||||||
expect(textEditor).toBeInstanceOf(TextEditor);
|
expect(textEditor).toBeInstanceOf(TextEditor);
|
||||||
textEditor.dispose();
|
textEditor.dispose();
|
||||||
|
@ -51,7 +51,7 @@ describe('TextEditor', () => {
|
||||||
expect(textEditor.element).toBe(null);
|
expect(textEditor.element).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('TextEditor focus should focus the contenteditable element', () => {
|
test("TextEditor focus should focus the contenteditable element", () => {
|
||||||
const textEditorElement = document.createElement("div");
|
const textEditorElement = document.createElement("div");
|
||||||
document.body.appendChild(textEditorElement);
|
document.body.appendChild(textEditorElement);
|
||||||
const textEditor = new TextEditor(textEditorElement);
|
const textEditor = new TextEditor(textEditorElement);
|
||||||
|
@ -76,8 +76,8 @@ describe('TextEditor', () => {
|
||||||
const textEditor = new TextEditor(textEditorElement);
|
const textEditor = new TextEditor(textEditorElement);
|
||||||
textEditor.root = textEditor.createRoot([
|
textEditor.root = textEditor.createRoot([
|
||||||
textEditor.createParagraph([
|
textEditor.createParagraph([
|
||||||
textEditor.createInlineFromString("Hello, World!")
|
textEditor.createInlineFromString("Hello, World!"),
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
expect(textEditor).toBeInstanceOf(TextEditor);
|
expect(textEditor).toBeInstanceOf(TextEditor);
|
||||||
textEditor.focus();
|
textEditor.focus();
|
|
@ -6,7 +6,7 @@
|
||||||
* Copyright (c) KALEIDOS INC
|
* Copyright (c) KALEIDOS INC
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mapContentFragmentFromHTML, mapContentFragmentFromString } from '../content/dom/Content.js';
|
import { mapContentFragmentFromHTML, mapContentFragmentFromString } from "../content/dom/Content.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the user pastes some HTML, what we do is generate
|
* When the user pastes some HTML, what we do is generate
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from "vitest";
|
||||||
import CommandMutations from './CommandMutations';
|
import CommandMutations from "./CommandMutations.js";
|
||||||
|
|
||||||
describe("CommandMutations", () => {
|
describe("CommandMutations", () => {
|
||||||
test("should create a new CommandMutations", () => {
|
test("should create a new CommandMutations", () => {
|
||||||
|
@ -67,5 +67,5 @@ describe("CommandMutations", () => {
|
||||||
expect(mutations.added).toBe(null);
|
expect(mutations.added).toBe(null);
|
||||||
expect(mutations.updated).toBe(null);
|
expect(mutations.updated).toBe(null);
|
||||||
expect(mutations.removed).toBe(null);
|
expect(mutations.removed).toBe(null);
|
||||||
})
|
});
|
||||||
});
|
});
|
|
@ -1,23 +1,26 @@
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from "vitest";
|
||||||
import { mapContentFragmentFromHTML, mapContentFragmentFromString } from './Content.js';
|
import {
|
||||||
|
mapContentFragmentFromHTML,
|
||||||
|
mapContentFragmentFromString,
|
||||||
|
} from "./Content.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe('Content', () => {
|
describe("Content", () => {
|
||||||
test("mapContentFragmentFromHTML should return a valid content for the editor", () => {
|
test("mapContentFragmentFromHTML should return a valid content for the editor", () => {
|
||||||
const inertElement = document.createElement("div");
|
const inertElement = document.createElement("div");
|
||||||
const contentFragment = mapContentFragmentFromHTML(
|
const contentFragment = mapContentFragmentFromHTML(
|
||||||
"<div>Hello, World!</div>",
|
"<div>Hello, World!</div>",
|
||||||
inertElement.style
|
inertElement.style,
|
||||||
);
|
);
|
||||||
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
||||||
expect(contentFragment.children).toHaveLength(1);
|
expect(contentFragment.children).toHaveLength(1);
|
||||||
expect(contentFragment.firstElementChild).toBeInstanceOf(HTMLDivElement);
|
expect(contentFragment.firstElementChild).toBeInstanceOf(HTMLDivElement);
|
||||||
expect(contentFragment.firstElementChild.firstElementChild).toBeInstanceOf(
|
expect(contentFragment.firstElementChild.firstElementChild).toBeInstanceOf(
|
||||||
HTMLSpanElement
|
HTMLSpanElement,
|
||||||
);
|
|
||||||
expect(contentFragment.firstElementChild.firstElementChild.firstChild).toBeInstanceOf(
|
|
||||||
Text
|
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
contentFragment.firstElementChild.firstElementChild.firstChild,
|
||||||
|
).toBeInstanceOf(Text);
|
||||||
expect(contentFragment.textContent).toBe("Hello, World!");
|
expect(contentFragment.textContent).toBe("Hello, World!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,18 +28,18 @@ describe('Content', () => {
|
||||||
const inertElement = document.createElement("div");
|
const inertElement = document.createElement("div");
|
||||||
const contentFragment = mapContentFragmentFromHTML(
|
const contentFragment = mapContentFragmentFromHTML(
|
||||||
"<div>Hello,<br/><span> World!</span><br/></div>",
|
"<div>Hello,<br/><span> World!</span><br/></div>",
|
||||||
inertElement.style
|
inertElement.style,
|
||||||
);
|
);
|
||||||
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
||||||
expect(contentFragment.children).toHaveLength(1);
|
expect(contentFragment.children).toHaveLength(1);
|
||||||
expect(contentFragment.firstElementChild).toBeInstanceOf(HTMLDivElement);
|
expect(contentFragment.firstElementChild).toBeInstanceOf(HTMLDivElement);
|
||||||
expect(contentFragment.firstElementChild.children).toHaveLength(2);
|
expect(contentFragment.firstElementChild.children).toHaveLength(2);
|
||||||
expect(contentFragment.firstElementChild.firstElementChild).toBeInstanceOf(
|
expect(contentFragment.firstElementChild.firstElementChild).toBeInstanceOf(
|
||||||
HTMLSpanElement
|
HTMLSpanElement,
|
||||||
);
|
|
||||||
expect(contentFragment.firstElementChild.firstElementChild.firstChild).toBeInstanceOf(
|
|
||||||
Text
|
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
contentFragment.firstElementChild.firstElementChild.firstChild,
|
||||||
|
).toBeInstanceOf(Text);
|
||||||
expect(contentFragment.textContent).toBe("Hello, World!");
|
expect(contentFragment.textContent).toBe("Hello, World!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,42 +52,46 @@ describe('Content', () => {
|
||||||
const inertElement = document.createElement("div");
|
const inertElement = document.createElement("div");
|
||||||
const contentFragment = mapContentFragmentFromHTML(
|
const contentFragment = mapContentFragmentFromHTML(
|
||||||
"<div>Lorem ipsum</div><div>Dolor sit amet</div><div><br/></div><div>Sed iaculis blandit odio ornare sagittis.</div>",
|
"<div>Lorem ipsum</div><div>Dolor sit amet</div><div><br/></div><div>Sed iaculis blandit odio ornare sagittis.</div>",
|
||||||
inertElement.style
|
inertElement.style,
|
||||||
);
|
);
|
||||||
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
||||||
expect(contentFragment.children).toHaveLength(3);
|
expect(contentFragment.children).toHaveLength(3);
|
||||||
for (let index = 0; index < contentFragment.children.length; index++) {
|
for (let index = 0; index < contentFragment.children.length; index++) {
|
||||||
expect(contentFragment.children.item(index)).toBeInstanceOf(HTMLDivElement);
|
expect(contentFragment.children.item(index)).toBeInstanceOf(
|
||||||
expect(contentFragment.children.item(index).firstElementChild).toBeInstanceOf(
|
HTMLDivElement,
|
||||||
HTMLSpanElement
|
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
contentFragment.children.item(index).firstElementChild.firstChild
|
contentFragment.children.item(index).firstElementChild,
|
||||||
|
).toBeInstanceOf(HTMLSpanElement);
|
||||||
|
expect(
|
||||||
|
contentFragment.children.item(index).firstElementChild.firstChild,
|
||||||
).toBeInstanceOf(Text);
|
).toBeInstanceOf(Text);
|
||||||
expect(contentFragment.children.item(index).textContent).toBe(paragraphs[index]);
|
expect(contentFragment.children.item(index).textContent).toBe(
|
||||||
|
paragraphs[index],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
expect(contentFragment.textContent).toBe("Lorem ipsumDolor sit ametSed iaculis blandit odio ornare sagittis.");
|
expect(contentFragment.textContent).toBe(
|
||||||
|
"Lorem ipsumDolor sit ametSed iaculis blandit odio ornare sagittis.",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("mapContentFragmentFromString should return a valid content for the editor", () => {
|
test("mapContentFragmentFromString should return a valid content for the editor", () => {
|
||||||
const contentFragment = mapContentFragmentFromString(
|
const contentFragment = mapContentFragmentFromString("Hello, \nWorld!");
|
||||||
"Hello, \nWorld!"
|
|
||||||
);
|
|
||||||
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
expect(contentFragment).toBeInstanceOf(DocumentFragment);
|
||||||
expect(contentFragment.children).toHaveLength(2);
|
expect(contentFragment.children).toHaveLength(2);
|
||||||
expect(contentFragment.children.item(0)).toBeInstanceOf(HTMLDivElement);
|
expect(contentFragment.children.item(0)).toBeInstanceOf(HTMLDivElement);
|
||||||
expect(contentFragment.children.item(1)).toBeInstanceOf(HTMLDivElement);
|
expect(contentFragment.children.item(1)).toBeInstanceOf(HTMLDivElement);
|
||||||
expect(contentFragment.children.item(0).firstElementChild).toBeInstanceOf(
|
expect(contentFragment.children.item(0).firstElementChild).toBeInstanceOf(
|
||||||
HTMLSpanElement
|
HTMLSpanElement,
|
||||||
);
|
|
||||||
expect(contentFragment.children.item(0).firstElementChild.firstChild).toBeInstanceOf(
|
|
||||||
Text
|
|
||||||
);
|
|
||||||
expect(contentFragment.children.item(1).firstElementChild).toBeInstanceOf(
|
|
||||||
HTMLSpanElement
|
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
contentFragment.children.item(1).firstElementChild.firstChild
|
contentFragment.children.item(0).firstElementChild.firstChild,
|
||||||
|
).toBeInstanceOf(Text);
|
||||||
|
expect(contentFragment.children.item(1).firstElementChild).toBeInstanceOf(
|
||||||
|
HTMLSpanElement,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
contentFragment.children.item(1).firstElementChild.firstChild,
|
||||||
).toBeInstanceOf(Text);
|
).toBeInstanceOf(Text);
|
||||||
expect(contentFragment.textContent).toBe("Hello, World!");
|
expect(contentFragment.textContent).toBe("Hello, World!");
|
||||||
});
|
});
|
|
@ -1,11 +1,17 @@
|
||||||
import { describe, test, expect } from "vitest";
|
import { describe, test, expect } from "vitest";
|
||||||
import { createElement, isElement, createRandomId, isOffsetAtStart, isOffsetAtEnd } from "./Element.js";
|
import {
|
||||||
|
createElement,
|
||||||
|
isElement,
|
||||||
|
createRandomId,
|
||||||
|
isOffsetAtStart,
|
||||||
|
isOffsetAtEnd,
|
||||||
|
} from "./Element.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe("Element", () => {
|
describe("Element", () => {
|
||||||
test("createRandomId should create a new random id", () => {
|
test("createRandomId should create a new random id", () => {
|
||||||
const randomId = createRandomId();
|
const randomId = createRandomId();
|
||||||
expect(typeof randomId).toBe('string');
|
expect(typeof randomId).toBe("string");
|
||||||
expect(randomId.length).toBeGreaterThan(0);
|
expect(randomId.length).toBeGreaterThan(0);
|
||||||
expect(randomId.length).toBeLessThan(12);
|
expect(randomId.length).toBeLessThan(12);
|
||||||
});
|
});
|
||||||
|
@ -20,8 +26,8 @@ describe("Element", () => {
|
||||||
const element = createElement("div", {
|
const element = createElement("div", {
|
||||||
attributes: {
|
attributes: {
|
||||||
"aria-multiline": true,
|
"aria-multiline": true,
|
||||||
"role": "textbox"
|
role: "textbox",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
expect(element.ariaMultiLine).toBe("true");
|
expect(element.ariaMultiLine).toBe("true");
|
||||||
expect(element.role).toBe("textbox");
|
expect(element.role).toBe("textbox");
|
||||||
|
@ -30,8 +36,8 @@ describe("Element", () => {
|
||||||
test("createElement should create a new element with data- properties", () => {
|
test("createElement should create a new element with data- properties", () => {
|
||||||
const element = createElement("div", {
|
const element = createElement("div", {
|
||||||
data: {
|
data: {
|
||||||
itype: "root"
|
itype: "root",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
expect(element.dataset.itype).toBe("root");
|
expect(element.dataset.itype).toBe("root");
|
||||||
});
|
});
|
||||||
|
@ -41,14 +47,14 @@ describe("Element", () => {
|
||||||
styles: {
|
styles: {
|
||||||
"text-decoration": "underline",
|
"text-decoration": "underline",
|
||||||
},
|
},
|
||||||
allowedStyles: [["text-decoration"]]
|
allowedStyles: [["text-decoration"]],
|
||||||
});
|
});
|
||||||
expect(element.style.textDecoration).toBe("underline");
|
expect(element.style.textDecoration).toBe("underline");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("createElement should create a new element with a child", () => {
|
test("createElement should create a new element with a child", () => {
|
||||||
const element = createElement("div", {
|
const element = createElement("div", {
|
||||||
children: new Text("Hello, World!")
|
children: new Text("Hello, World!"),
|
||||||
});
|
});
|
||||||
expect(element.textContent).toBe("Hello, World!");
|
expect(element.textContent).toBe("Hello, World!");
|
||||||
});
|
});
|
||||||
|
@ -59,16 +65,18 @@ describe("Element", () => {
|
||||||
createElement("div", {
|
createElement("div", {
|
||||||
children: [
|
children: [
|
||||||
createElement("div", {
|
createElement("div", {
|
||||||
children: new Text("Hello, World!")
|
children: new Text("Hello, World!"),
|
||||||
})
|
}),
|
||||||
]
|
],
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(element.textContent).toBe("Hello, World!");
|
expect(element.textContent).toBe("Hello, World!");
|
||||||
expect(element.firstChild.nodeType).toBe(Node.ELEMENT_NODE);
|
expect(element.firstChild.nodeType).toBe(Node.ELEMENT_NODE);
|
||||||
expect(element.firstChild.firstChild.nodeType).toBe(Node.ELEMENT_NODE);
|
expect(element.firstChild.firstChild.nodeType).toBe(Node.ELEMENT_NODE);
|
||||||
expect(element.firstChild.firstChild.firstChild.nodeType).toBe(Node.TEXT_NODE);
|
expect(element.firstChild.firstChild.firstChild.nodeType).toBe(
|
||||||
|
Node.TEXT_NODE,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("isElement returns true if the passed element is the expected element", () => {
|
test("isElement returns true if the passed element is the expected element", () => {
|
||||||
|
@ -81,9 +89,9 @@ describe("Element", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("isOffsetAtStart should return true when offset is 0", () => {
|
test("isOffsetAtStart should return true when offset is 0", () => {
|
||||||
const element = createElement('span', {
|
const element = createElement("span", {
|
||||||
children: new Text("Hello")
|
children: new Text("Hello"),
|
||||||
})
|
});
|
||||||
expect(isOffsetAtStart(element, 0)).toBe(true);
|
expect(isOffsetAtStart(element, 0)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,24 @@
|
||||||
import { describe, test, expect } from "vitest";
|
import { describe, test, expect } from "vitest";
|
||||||
import { createEmptyInline, createInline, getInline, getInlineLength, isInline, isInlineEnd, isInlineStart, isLikeInline, splitInline, TAG, TYPE } from "./Inline.js";
|
import {
|
||||||
|
createEmptyInline,
|
||||||
|
createInline,
|
||||||
|
getInline,
|
||||||
|
getInlineLength,
|
||||||
|
isInline,
|
||||||
|
isInlineEnd,
|
||||||
|
isInlineStart,
|
||||||
|
isLikeInline,
|
||||||
|
splitInline,
|
||||||
|
TAG,
|
||||||
|
TYPE,
|
||||||
|
} from "./Inline.js";
|
||||||
import { createLineBreak } from "./LineBreak.js";
|
import { createLineBreak } from "./LineBreak.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe("Inline", () => {
|
describe("Inline", () => {
|
||||||
test("createInline should throw when passed an invalid child", () => {
|
test("createInline should throw when passed an invalid child", () => {
|
||||||
expect(() => createInline("Hello, World!")).toThrowError(
|
expect(() => createInline("Hello, World!")).toThrowError(
|
||||||
"Invalid inline child"
|
"Invalid inline child",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,7 +56,7 @@ describe("Inline", () => {
|
||||||
expect(isInline(a)).toBe(false);
|
expect(isInline(a)).toBe(false);
|
||||||
const b = null;
|
const b = null;
|
||||||
expect(isInline(b)).toBe(false);
|
expect(isInline(b)).toBe(false);
|
||||||
const c = document.createElement('span');
|
const c = document.createElement("span");
|
||||||
expect(isInline(c)).toBe(false);
|
expect(isInline(c)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,11 +94,11 @@ describe("Inline", () => {
|
||||||
|
|
||||||
test("getInline ", () => {
|
test("getInline ", () => {
|
||||||
expect(getInline(null)).toBe(null);
|
expect(getInline(null)).toBe(null);
|
||||||
})
|
});
|
||||||
|
|
||||||
test("getInlineLength throws when the passed node is not an inline", () => {
|
test("getInlineLength throws when the passed node is not an inline", () => {
|
||||||
const inline = document.createElement('div');
|
const inline = document.createElement("div");
|
||||||
expect(() => getInlineLength(inline)).toThrowError('Invalid inline');
|
expect(() => getInlineLength(inline)).toThrowError("Invalid inline");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getInlineLength returns the length of the inline content", () => {
|
test("getInlineLength returns the length of the inline content", () => {
|
|
@ -1,11 +1,11 @@
|
||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from "vitest";
|
||||||
import { createLineBreak } from './LineBreak.js';
|
import { createLineBreak } from "./LineBreak.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe('LineBreak', () => {
|
describe("LineBreak", () => {
|
||||||
test("createLineBreak should return a <br> element", () => {
|
test("createLineBreak should return a <br> element", () => {
|
||||||
const br = createLineBreak();
|
const br = createLineBreak();
|
||||||
expect(br.nodeType).toBe(Node.ELEMENT_NODE);
|
expect(br.nodeType).toBe(Node.ELEMENT_NODE);
|
||||||
expect(br.nodeName).toBe('BR');
|
expect(br.nodeName).toBe("BR");
|
||||||
})
|
});
|
||||||
});
|
});
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
createRandomId,
|
||||||
createElement,
|
createElement,
|
||||||
isElement,
|
isElement,
|
||||||
isOffsetAtStart,
|
isOffsetAtStart,
|
||||||
|
@ -24,8 +25,6 @@ import {
|
||||||
} from "./Inline.js";
|
} from "./Inline.js";
|
||||||
import { createLineBreak, isLineBreak } from "./LineBreak.js";
|
import { createLineBreak, isLineBreak } from "./LineBreak.js";
|
||||||
import { setStyles } from "./Style.js";
|
import { setStyles } from "./Style.js";
|
||||||
import { createRandomId } from "./Element.js";
|
|
||||||
import { isEmptyTextNode, isTextNode } from './TextNode.js';
|
|
||||||
|
|
||||||
export const TAG = "DIV";
|
export const TAG = "DIV";
|
||||||
export const TYPE = "paragraph";
|
export const TYPE = "paragraph";
|
|
@ -18,9 +18,9 @@ import { createInline, isInline } from "./Inline.js";
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe("Paragraph", () => {
|
describe("Paragraph", () => {
|
||||||
test("createParagraph should throw when passed invalid children", () => {
|
test("createParagraph should throw when passed invalid children", () => {
|
||||||
expect(() => createParagraph([
|
expect(() => createParagraph(["Whatever"])).toThrowError(
|
||||||
"Whatever"
|
"Invalid paragraph children",
|
||||||
])).toThrowError("Invalid paragraph children");
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("createEmptyParagraph should create a new empty paragraph", () => {
|
test("createEmptyParagraph should create a new empty paragraph", () => {
|
||||||
|
@ -33,30 +33,30 @@ describe("Paragraph", () => {
|
||||||
|
|
||||||
test("isParagraph should return true when the passed node is a paragraph", () => {
|
test("isParagraph should return true when the passed node is a paragraph", () => {
|
||||||
expect(isParagraph(null)).toBe(false);
|
expect(isParagraph(null)).toBe(false);
|
||||||
expect(isParagraph(document.createElement('div'))).toBe(false);
|
expect(isParagraph(document.createElement("div"))).toBe(false);
|
||||||
expect(isParagraph(document.createElement('h1'))).toBe(false);
|
expect(isParagraph(document.createElement("h1"))).toBe(false);
|
||||||
expect(isParagraph(createEmptyParagraph())).toBe(true);
|
expect(isParagraph(createEmptyParagraph())).toBe(true);
|
||||||
expect(isParagraph(createParagraph([
|
expect(
|
||||||
createInline(new Text('Hello, World!'))
|
isParagraph(createParagraph([createInline(new Text("Hello, World!"))])),
|
||||||
]))).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("isLikeParagraph should return true when node looks like a paragraph", () => {
|
test("isLikeParagraph should return true when node looks like a paragraph", () => {
|
||||||
const p = document.createElement('p');
|
const p = document.createElement("p");
|
||||||
expect(isLikeParagraph(p)).toBe(true);
|
expect(isLikeParagraph(p)).toBe(true);
|
||||||
const div = document.createElement('div');
|
const div = document.createElement("div");
|
||||||
expect(isLikeParagraph(div)).toBe(true);
|
expect(isLikeParagraph(div)).toBe(true);
|
||||||
const h1 = document.createElement('h1');
|
const h1 = document.createElement("h1");
|
||||||
expect(isLikeParagraph(h1)).toBe(true);
|
expect(isLikeParagraph(h1)).toBe(true);
|
||||||
const h2 = document.createElement('h2');
|
const h2 = document.createElement("h2");
|
||||||
expect(isLikeParagraph(h2)).toBe(true);
|
expect(isLikeParagraph(h2)).toBe(true);
|
||||||
const h3 = document.createElement('h3');
|
const h3 = document.createElement("h3");
|
||||||
expect(isLikeParagraph(h3)).toBe(true);
|
expect(isLikeParagraph(h3)).toBe(true);
|
||||||
const h4 = document.createElement('h4');
|
const h4 = document.createElement("h4");
|
||||||
expect(isLikeParagraph(h4)).toBe(true);
|
expect(isLikeParagraph(h4)).toBe(true);
|
||||||
const h5 = document.createElement('h5');
|
const h5 = document.createElement("h5");
|
||||||
expect(isLikeParagraph(h5)).toBe(true);
|
expect(isLikeParagraph(h5)).toBe(true);
|
||||||
const h6 = document.createElement('h6');
|
const h6 = document.createElement("h6");
|
||||||
expect(isLikeParagraph(h6)).toBe(true);
|
expect(isLikeParagraph(h6)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ describe("Paragraph", () => {
|
||||||
|
|
||||||
test("getParagraph should return null if there aren't closer paragraph nodes", () => {
|
test("getParagraph should return null if there aren't closer paragraph nodes", () => {
|
||||||
const text = new Text("Hello, World!");
|
const text = new Text("Hello, World!");
|
||||||
const whatever = document.createElement('div');
|
const whatever = document.createElement("div");
|
||||||
whatever.appendChild(text);
|
whatever.appendChild(text);
|
||||||
expect(getParagraph(text)).toBe(null);
|
expect(getParagraph(text)).toBe(null);
|
||||||
});
|
});
|
||||||
|
@ -81,7 +81,7 @@ describe("Paragraph", () => {
|
||||||
|
|
||||||
test("isParagraphStart should return true on a paragraph", () => {
|
test("isParagraphStart should return true on a paragraph", () => {
|
||||||
const paragraph = createParagraph([
|
const paragraph = createParagraph([
|
||||||
createInline(new Text("Hello, World!"))
|
createInline(new Text("Hello, World!")),
|
||||||
]);
|
]);
|
||||||
expect(isParagraphStart(paragraph.firstChild.firstChild, 0)).toBe(true);
|
expect(isParagraphStart(paragraph.firstChild.firstChild, 0)).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -162,11 +162,10 @@ describe("Paragraph", () => {
|
||||||
|
|
||||||
const nonEmptyInline = document.createElement("span");
|
const nonEmptyInline = document.createElement("span");
|
||||||
nonEmptyInline.dataset.itype = "inline";
|
nonEmptyInline.dataset.itype = "inline";
|
||||||
nonEmptyInline.appendChild(new Text('Not empty!'));
|
nonEmptyInline.appendChild(new Text("Not empty!"));
|
||||||
const nonEmptyParagraph = document.createElement("div");
|
const nonEmptyParagraph = document.createElement("div");
|
||||||
nonEmptyParagraph.dataset.itype = "paragraph";
|
nonEmptyParagraph.dataset.itype = "paragraph";
|
||||||
nonEmptyParagraph.appendChild(nonEmptyInline);
|
nonEmptyParagraph.appendChild(nonEmptyInline);
|
||||||
expect(isEmptyParagraph(nonEmptyParagraph)).toBe(false);
|
expect(isEmptyParagraph(nonEmptyParagraph)).toBe(false);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -6,10 +6,9 @@
|
||||||
* Copyright (c) KALEIDOS INC
|
* Copyright (c) KALEIDOS INC
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createElement, isElement } from "./Element.js";
|
import { createRandomId, createElement, isElement } from "./Element.js";
|
||||||
import { createEmptyParagraph, isParagraph } from "./Paragraph.js";
|
import { createEmptyParagraph, isParagraph } from "./Paragraph.js";
|
||||||
import { setStyles } from "./Style.js";
|
import { setStyles } from "./Style.js";
|
||||||
import { createRandomId } from "./Element.js";
|
|
||||||
|
|
||||||
export const TAG = "DIV";
|
export const TAG = "DIV";
|
||||||
export const TYPE = "root";
|
export const TYPE = "root";
|
|
@ -1,11 +1,11 @@
|
||||||
import { describe, test, expect } from "vitest";
|
import { describe, test, expect } from "vitest";
|
||||||
import { createEmptyRoot, createRoot, setRootStyles, TAG, TYPE } from './Root.js'
|
import { createEmptyRoot, createRoot, setRootStyles, TAG, TYPE } from "./Root.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe("Root", () => {
|
describe("Root", () => {
|
||||||
test("createRoot should throw when passed invalid children", () => {
|
test("createRoot should throw when passed invalid children", () => {
|
||||||
expect(() => createRoot(["Whatever"])).toThrowError(
|
expect(() => createRoot(["Whatever"])).toThrowError(
|
||||||
"Invalid root children"
|
"Invalid root children",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -16,18 +16,20 @@ describe("Root", () => {
|
||||||
expect(emptyRoot.dataset.itype).toBe(TYPE);
|
expect(emptyRoot.dataset.itype).toBe(TYPE);
|
||||||
expect(emptyRoot.firstChild).toBeInstanceOf(HTMLDivElement);
|
expect(emptyRoot.firstChild).toBeInstanceOf(HTMLDivElement);
|
||||||
expect(emptyRoot.firstChild.firstChild).toBeInstanceOf(HTMLSpanElement);
|
expect(emptyRoot.firstChild.firstChild).toBeInstanceOf(HTMLSpanElement);
|
||||||
expect(emptyRoot.firstChild.firstChild.firstChild).toBeInstanceOf(HTMLBRElement);
|
expect(emptyRoot.firstChild.firstChild.firstChild).toBeInstanceOf(
|
||||||
|
HTMLBRElement,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("setRootStyles should apply only the styles of root to the root", () => {
|
test("setRootStyles should apply only the styles of root to the root", () => {
|
||||||
const emptyRoot = createEmptyRoot();
|
const emptyRoot = createEmptyRoot();
|
||||||
setRootStyles(emptyRoot, {
|
setRootStyles(emptyRoot, {
|
||||||
["--vertical-align"]: "top",
|
["--vertical-align"]: "top",
|
||||||
["font-size"]: "25px"
|
["font-size"]: "25px",
|
||||||
});
|
});
|
||||||
expect(emptyRoot.style.getPropertyValue("--vertical-align")).toBe("top");
|
expect(emptyRoot.style.getPropertyValue("--vertical-align")).toBe("top");
|
||||||
// We expect this style to be empty because we don't apply it
|
// We expect this style to be empty because we don't apply it
|
||||||
// to the root.
|
// to the root.
|
||||||
expect(emptyRoot.style.getPropertyValue("font-size")).toBe("");
|
expect(emptyRoot.style.getPropertyValue("font-size")).toBe("");
|
||||||
})
|
});
|
||||||
});
|
});
|
|
@ -1,5 +1,11 @@
|
||||||
import { describe, test, expect, vi } from "vitest";
|
import { describe, test, expect, vi } from "vitest";
|
||||||
import { getStyles, isDisplayBlock, isDisplayInline, setStyle, setStyles } from "./Style.js";
|
import {
|
||||||
|
getStyles,
|
||||||
|
isDisplayBlock,
|
||||||
|
isDisplayInline,
|
||||||
|
setStyle,
|
||||||
|
setStyles,
|
||||||
|
} from "./Style.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe("Style", () => {
|
describe("Style", () => {
|
||||||
|
@ -39,16 +45,16 @@ describe("Style", () => {
|
||||||
|
|
||||||
test("getStyles should retrieve a list of allowed styles", () => {
|
test("getStyles should retrieve a list of allowed styles", () => {
|
||||||
const element = document.createElement("div");
|
const element = document.createElement("div");
|
||||||
element.style.display = 'block';
|
element.style.display = "block";
|
||||||
element.style.textDecoration = 'underline';
|
element.style.textDecoration = "underline";
|
||||||
element.style.fontSize = '32px';
|
element.style.fontSize = "32px";
|
||||||
const textDecorationStyles = getStyles(element, [["text-decoration"]]);
|
const textDecorationStyles = getStyles(element, [["text-decoration"]]);
|
||||||
expect(textDecorationStyles).toStrictEqual({
|
expect(textDecorationStyles).toStrictEqual({
|
||||||
"text-decoration": "underline"
|
"text-decoration": "underline",
|
||||||
});
|
});
|
||||||
const displayStyles = getStyles(element, [["display"]]);
|
const displayStyles = getStyles(element, [["display"]]);
|
||||||
expect(displayStyles).toStrictEqual({
|
expect(displayStyles).toStrictEqual({
|
||||||
"display": "block",
|
display: "block",
|
||||||
});
|
});
|
||||||
const fontSizeStyles = getStyles(element, [["font-size", "px"]]);
|
const fontSizeStyles = getStyles(element, [["font-size", "px"]]);
|
||||||
expect(fontSizeStyles).toStrictEqual({
|
expect(fontSizeStyles).toStrictEqual({
|
|
@ -1,6 +1,6 @@
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from "vitest";
|
||||||
import { isTextNode, getTextNodeLength } from './TextNode.js';
|
import { isTextNode, getTextNodeLength } from "./TextNode.js";
|
||||||
import { createLineBreak } from './LineBreak.js';
|
import { createLineBreak } from "./LineBreak.js";
|
||||||
|
|
||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
describe("TextNode", () => {
|
describe("TextNode", () => {
|
||||||
|
@ -11,16 +11,18 @@ describe("TextNode", () => {
|
||||||
expect(isTextNode("hola")).toBe(false);
|
expect(isTextNode("hola")).toBe(false);
|
||||||
expect(isTextNode({})).toBe(false);
|
expect(isTextNode({})).toBe(false);
|
||||||
expect(isTextNode([])).toBe(false);
|
expect(isTextNode([])).toBe(false);
|
||||||
expect(() => isTextNode(undefined)).toThrowError('Invalid text node');
|
expect(() => isTextNode(undefined)).toThrowError("Invalid text node");
|
||||||
expect(() => isTextNode(null)).toThrowError('Invalid text node');
|
expect(() => isTextNode(null)).toThrowError("Invalid text node");
|
||||||
expect(() => isTextNode(0)).toThrowError('Invalid text node');
|
expect(() => isTextNode(0)).toThrowError("Invalid text node");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getTextNodeLength should return the length of the text node or 0 if it is a <br>", () => {
|
test("getTextNodeLength should return the length of the text node or 0 if it is a <br>", () => {
|
||||||
expect(getTextNodeLength(new Text("Hello, World!"))).toBe(13);
|
expect(getTextNodeLength(new Text("Hello, World!"))).toBe(13);
|
||||||
expect(getTextNodeLength(createLineBreak())).toBe(0);
|
expect(getTextNodeLength(createLineBreak())).toBe(0);
|
||||||
expect(() => getTextNodeLength(undefined)).toThrowError('Invalid text node');
|
expect(() => getTextNodeLength(undefined)).toThrowError(
|
||||||
expect(() => getTextNodeLength(null)).toThrowError('Invalid text node');
|
"Invalid text node",
|
||||||
expect(() => getTextNodeLength(0)).toThrowError('Invalid text node');
|
);
|
||||||
|
expect(() => getTextNodeLength(null)).toThrowError("Invalid text node");
|
||||||
|
expect(() => getTextNodeLength(0)).toThrowError("Invalid text node");
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -10,11 +10,11 @@ describe("TextNodeIterator", () => {
|
||||||
test("Create a new TextNodeIterator with an invalid root should throw", () => {
|
test("Create a new TextNodeIterator with an invalid root should throw", () => {
|
||||||
expect(() => new TextNodeIterator(null)).toThrowError("Invalid root node");
|
expect(() => new TextNodeIterator(null)).toThrowError("Invalid root node");
|
||||||
expect(() => new TextNodeIterator(Infinity)).toThrowError(
|
expect(() => new TextNodeIterator(Infinity)).toThrowError(
|
||||||
"Invalid root node"
|
"Invalid root node",
|
||||||
);
|
);
|
||||||
expect(() => new TextNodeIterator(1)).toThrowError("Invalid root node");
|
expect(() => new TextNodeIterator(1)).toThrowError("Invalid root node");
|
||||||
expect(() => new TextNodeIterator("hola")).toThrowError(
|
expect(() => new TextNodeIterator("hola")).toThrowError(
|
||||||
"Invalid root node"
|
"Invalid root node",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -26,7 +26,10 @@ describe("TextNodeIterator", () => {
|
||||||
createInline(new Text("Whatever")),
|
createInline(new Text("Whatever")),
|
||||||
]),
|
]),
|
||||||
createParagraph([createInline(createLineBreak())]),
|
createParagraph([createInline(createLineBreak())]),
|
||||||
createParagraph([createInline(new Text("This is a ")), createInline(new Text("test"))]),
|
createParagraph([
|
||||||
|
createInline(new Text("This is a ")),
|
||||||
|
createInline(new Text("test")),
|
||||||
|
]),
|
||||||
createParagraph([createInline(new Text("Hi!"))]),
|
createParagraph([createInline(new Text("Hi!"))]),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import { expect, describe, test, vi } from 'vitest'
|
import { expect, describe, test, vi } from "vitest";
|
||||||
import ChangeController from './ChangeController'
|
import ChangeController from "./ChangeController.js";
|
||||||
|
|
||||||
describe("ChangeController", () => {
|
describe("ChangeController", () => {
|
||||||
test("Creating a ChangeController without a valid time should throw", () => {
|
test("Creating a ChangeController without a valid time should throw", () => {
|
||||||
expect(() => new ChangeController(Infinity)).toThrowError('Invalid time')
|
expect(() => new ChangeController(Infinity)).toThrowError("Invalid time");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("A ChangeController should dispatch an event when `notifyImmediately` is called", () => {
|
test("A ChangeController should dispatch an event when `notifyImmediately` is called", () => {
|
||||||
const changeListener = vi.fn();
|
const changeListener = vi.fn();
|
||||||
const changeController = new ChangeController(10);
|
const changeController = new ChangeController(10);
|
||||||
changeController.addEventListener("change", changeListener)
|
changeController.addEventListener("change", changeListener);
|
||||||
changeController.notifyImmediately();
|
changeController.notifyImmediately();
|
||||||
expect(changeController.hasPendingChanges).toBe(false);
|
expect(changeController.hasPendingChanges).toBe(false);
|
||||||
expect(changeListener).toBeCalled(1);
|
expect(changeListener).toBeCalled(1);
|
|
@ -16,7 +16,6 @@ import {
|
||||||
isInlineStart,
|
isInlineStart,
|
||||||
isInlineEnd,
|
isInlineEnd,
|
||||||
setInlineStyles,
|
setInlineStyles,
|
||||||
mergeInlines,
|
|
||||||
splitInline,
|
splitInline,
|
||||||
createEmptyInline,
|
createEmptyInline,
|
||||||
} from "../content/dom/Inline.js";
|
} from "../content/dom/Inline.js";
|
||||||
|
@ -29,7 +28,6 @@ import {
|
||||||
isParagraphEnd,
|
isParagraphEnd,
|
||||||
setParagraphStyles,
|
setParagraphStyles,
|
||||||
splitParagraph,
|
splitParagraph,
|
||||||
splitParagraphAtNode,
|
|
||||||
mergeParagraphs,
|
mergeParagraphs,
|
||||||
fixParagraph,
|
fixParagraph,
|
||||||
} from "../content/dom/Paragraph.js";
|
} from "../content/dom/Paragraph.js";
|
||||||
|
@ -48,9 +46,6 @@ import { setRootStyles } from "../content/dom/Root.js";
|
||||||
import { SelectionDirection } from "./SelectionDirection.js";
|
import { SelectionDirection } from "./SelectionDirection.js";
|
||||||
import SafeGuard from "./SafeGuard.js";
|
import SafeGuard from "./SafeGuard.js";
|
||||||
|
|
||||||
const SAFE_GUARD = true;
|
|
||||||
const SAFE_GUARD_TIME = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supported options for the SelectionController.
|
* Supported options for the SelectionController.
|
||||||
*
|
*
|
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@ test("Create selection DOM rects from client rects", () => {
|
||||||
const rect = new DOMRect(20, 20, 100, 50);
|
const rect = new DOMRect(20, 20, 100, 50);
|
||||||
const clientRects = [
|
const clientRects = [
|
||||||
new DOMRect(20, 20, 100, 20),
|
new DOMRect(20, 20, 100, 20),
|
||||||
new DOMRect(20, 50, 50, 20)
|
new DOMRect(20, 50, 50, 20),
|
||||||
];
|
];
|
||||||
const fragment = createSelectionImposterFromClientRects(rect, clientRects);
|
const fragment = createSelectionImposterFromClientRects(rect, clientRects);
|
||||||
expect(fragment).toBeInstanceOf(DocumentFragment);
|
expect(fragment).toBeInstanceOf(DocumentFragment);
|
|
@ -1,7 +1,7 @@
|
||||||
import { createRoot } from "~/editor/content/dom/Root";
|
import { createRoot } from "../editor/content/dom/Root.js";
|
||||||
import { createParagraph } from "~/editor/content/dom/Paragraph";
|
import { createParagraph } from "../editor/content/dom/Paragraph.js";
|
||||||
import { createEmptyInline, createInline } from "~/editor/content/dom/Inline";
|
import { createEmptyInline, createInline } from "../editor/content/dom/Inline.js";
|
||||||
import { createLineBreak } from "~/editor/content/dom/LineBreak";
|
import { createLineBreak } from "../editor/content/dom/LineBreak.js";
|
||||||
|
|
||||||
export class TextEditorMock extends EventTarget {
|
export class TextEditorMock extends EventTarget {
|
||||||
/**
|
/**
|
|
@ -1,26 +1,23 @@
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import { coverageConfigDefaults } from 'vitest/config'
|
import { coverageConfigDefaults } from "vitest/config"
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
root: "./src",
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"~": resolve("."),
|
"~": resolve("./src"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
minify: false,
|
minify: true,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
lib: {
|
lib: {
|
||||||
entry: "editor/TextEditor.js",
|
entry: "src/editor/TextEditor.js",
|
||||||
name: "TextEditor",
|
name: "TextEditor",
|
||||||
fileName: "TextEditor",
|
fileName: "TextEditor",
|
||||||
formats: ["es"],
|
formats: ["es"],
|
||||||
},
|
},
|
||||||
terserOptions: {
|
|
||||||
compress: true,
|
|
||||||
mangle: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
coverage: {
|
coverage: {
|
||||||
|
|
|
@ -518,6 +518,7 @@ __metadata:
|
||||||
esbuild: "npm:^0.24.0"
|
esbuild: "npm:^0.24.0"
|
||||||
jsdom: "npm:^25.0.0"
|
jsdom: "npm:^25.0.0"
|
||||||
playwright: "npm:^1.45.1"
|
playwright: "npm:^1.45.1"
|
||||||
|
prettier: "npm:^3.3.3"
|
||||||
vite: "npm:^5.3.1"
|
vite: "npm:^5.3.1"
|
||||||
vitest: "npm:^1.6.0"
|
vitest: "npm:^1.6.0"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
|
@ -2292,6 +2293,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"prettier@npm:^3.3.3":
|
||||||
|
version: 3.3.3
|
||||||
|
resolution: "prettier@npm:3.3.3"
|
||||||
|
bin:
|
||||||
|
prettier: bin/prettier.cjs
|
||||||
|
checksum: 10c0/b85828b08e7505716324e4245549b9205c0cacb25342a030ba8885aba2039a115dbcf75a0b7ca3b37bc9d101ee61fab8113fc69ca3359f2a226f1ecc07ad2e26
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"pretty-format@npm:^29.7.0":
|
"pretty-format@npm:^29.7.0":
|
||||||
version: 29.7.0
|
version: 29.7.0
|
||||||
resolution: "pretty-format@npm:29.7.0"
|
resolution: "pretty-format@npm:29.7.0"
|
||||||
|
|
|
@ -396,6 +396,10 @@ msgstr "The token has no expiration date"
|
||||||
msgid "dashboard.add-shared"
|
msgid "dashboard.add-shared"
|
||||||
msgstr "Add as Shared Library"
|
msgstr "Add as Shared Library"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/main_menu.cljs:607
|
||||||
|
msgid "dashboard.show-version-history"
|
||||||
|
msgstr "Version history"
|
||||||
|
|
||||||
#: src/app/main/ui/settings/profile.cljs:72
|
#: src/app/main/ui/settings/profile.cljs:72
|
||||||
msgid "dashboard.change-email"
|
msgid "dashboard.change-email"
|
||||||
msgstr "Change email"
|
msgstr "Change email"
|
||||||
|
|
|
@ -398,6 +398,10 @@ msgstr "El token no tiene fecha de expiración"
|
||||||
msgid "dashboard.add-shared"
|
msgid "dashboard.add-shared"
|
||||||
msgstr "Añadir como Biblioteca Compartida"
|
msgstr "Añadir como Biblioteca Compartida"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/main_menu.cljs:607
|
||||||
|
msgid "dashboard.show-version-history"
|
||||||
|
msgstr "Histórico de versiones"
|
||||||
|
|
||||||
#: src/app/main/ui/settings/profile.cljs:72
|
#: src/app/main/ui/settings/profile.cljs:72
|
||||||
msgid "dashboard.change-email"
|
msgid "dashboard.change-email"
|
||||||
msgstr "Cambiar correo"
|
msgstr "Cambiar correo"
|
||||||
|
|
Loading…
Add table
Reference in a new issue