Vendored
+1
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"atrule",
|
"atrule",
|
||||||
|
"Chainable",
|
||||||
"colspan",
|
"colspan",
|
||||||
"compositionend",
|
"compositionend",
|
||||||
"compositionstart",
|
"compositionstart",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"video": false,
|
||||||
|
"viewportWidth": 1080,
|
||||||
|
"viewportHeight": 720
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"name": "canvas-editor"
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
declare namespace Editor {
|
||||||
|
import('../src/editor/index')
|
||||||
|
import Editor from '../src/editor/index'
|
||||||
|
}
|
||||||
|
|
||||||
|
declare namespace Cypress {
|
||||||
|
|
||||||
|
interface Chainable {
|
||||||
|
|
||||||
|
getEditor(): Chainable<Editor>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import Editor from '../../src/editor'
|
||||||
|
|
||||||
|
describe('基础功能', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
|
||||||
|
it('编辑保存', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
expect(payload.data[0].value).to.eq(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
cy.get('@canvas').type(text)
|
||||||
|
|
||||||
|
cy.get('@canvas').type(`{ctrl}s`)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('模式切换', () => {
|
||||||
|
cy.get('@canvas').click()
|
||||||
|
|
||||||
|
cy.get('.cursor').should('have.css', 'display', 'block')
|
||||||
|
|
||||||
|
cy.get('.editor-mode').click().click()
|
||||||
|
|
||||||
|
cy.get('.editor-mode').contains('只读')
|
||||||
|
|
||||||
|
cy.get('@canvas').click()
|
||||||
|
|
||||||
|
cy.get('.cursor').should('have.css', 'display', 'none')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('页面缩放', () => {
|
||||||
|
|
||||||
|
cy.get('.page-scale-add').click()
|
||||||
|
|
||||||
|
cy.get('.page-scale-percentage').contains('110%')
|
||||||
|
|
||||||
|
cy.get('.page-scale-minus').click().click()
|
||||||
|
|
||||||
|
cy.get('.page-scale-percentage').contains('90%')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-代码块', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = `console.log('canvas-editor')`
|
||||||
|
|
||||||
|
it('代码块', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data[2]
|
||||||
|
|
||||||
|
expect(data.value).to.eq('log')
|
||||||
|
|
||||||
|
expect(data.color).to.eq('#b9a40a')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
cy.get('.menu-item__codeblock').click()
|
||||||
|
|
||||||
|
cy.get('.dialog-option [name="codeblock"]').type(text)
|
||||||
|
|
||||||
|
cy.get('.dialog-menu button').eq(1).click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-清除格式', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
const textLength = text.length
|
||||||
|
|
||||||
|
it('清除格式', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].italic).to.eq(false)
|
||||||
|
|
||||||
|
expect(data[0].bold).to.eq(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text,
|
||||||
|
bold: true,
|
||||||
|
italic: true
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__format')
|
||||||
|
.click()
|
||||||
|
.then(() => {
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-超链接', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
const url = 'https://hufe.club/canvas-editor'
|
||||||
|
|
||||||
|
it('超链接', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].type).to.eq('hyperlink')
|
||||||
|
|
||||||
|
expect(data[0].url).to.eq(url)
|
||||||
|
|
||||||
|
expect(data[0]?.valueList?.[0].value).to.eq(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
cy.get('.menu-item__hyperlink').click()
|
||||||
|
|
||||||
|
cy.get('.dialog-option__item [name="name"]').type(text)
|
||||||
|
|
||||||
|
cy.get('.dialog-option__item [name="url"]').type(url)
|
||||||
|
|
||||||
|
cy.get('.dialog-menu button').eq(1).click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-图片', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('图片', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
expect(data[0].type).to.eq('image')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
cy.get('#image').attachFile('test.png')
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
describe('菜单-分页符', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('分页符', () => {
|
||||||
|
cy.get('@canvas').click()
|
||||||
|
|
||||||
|
cy.get('.menu-item__page-break').click().click()
|
||||||
|
|
||||||
|
cy.get('canvas').should('have.length', 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-格式刷', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
const textLength = text.length
|
||||||
|
|
||||||
|
it('格式刷', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data.length).to.eq(1)
|
||||||
|
|
||||||
|
expect(data[0].italic).to.eq(true)
|
||||||
|
|
||||||
|
expect(data[0].bold).to.eq(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text,
|
||||||
|
bold: true,
|
||||||
|
italic: true
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__painter')
|
||||||
|
.click()
|
||||||
|
.then(() => {
|
||||||
|
editor.command.executeSetRange(textLength, 2 * textLength)
|
||||||
|
|
||||||
|
editor.command.executeApplyPainterStyle()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-行处理', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
|
||||||
|
it('左对齐', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].rowFlex).to.eq('left')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
cy.get('.menu-item__left')
|
||||||
|
.click()
|
||||||
|
.then(() => {
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('居中对齐', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].rowFlex).to.eq('center')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
cy.get('.menu-item__center')
|
||||||
|
.click()
|
||||||
|
.then(() => {
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('靠右对齐', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].rowFlex).to.eq('right')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
cy.get('.menu-item__right')
|
||||||
|
.click()
|
||||||
|
.then(() => {
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('行间距', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].rowMargin).to.eq(1.25)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
cy.get('.menu-item__row-margin').as('rowMargin').click()
|
||||||
|
|
||||||
|
cy.get('@rowMargin').find('li').eq(1).click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-分割线', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('分割线', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].type).to.eq('separator')
|
||||||
|
|
||||||
|
expect(data[0]?.dashArray?.[0]).to.eq(1)
|
||||||
|
|
||||||
|
expect(data[0]?.dashArray?.[1]).to.eq(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
cy.get('.menu-item__separator').click()
|
||||||
|
|
||||||
|
cy.get('.menu-item__separator li').eq(1).click()
|
||||||
|
.then(() => {
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-表格', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('表格', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].type).to.eq('table')
|
||||||
|
|
||||||
|
expect(data[0].trList?.length).to.eq(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertTable(8, 8)
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-文本处理', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
const textLength = text.length
|
||||||
|
|
||||||
|
it('字体', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].font).to.eq('宋体')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__font').as('font').click()
|
||||||
|
|
||||||
|
cy.get('@font').find('li').eq(1).click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('字体增大', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].size).to.eq(18)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__size-add').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('字体减小', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].size).to.eq(14)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__size-minus').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('加粗', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].bold).to.eq(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__bold').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('斜体', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].italic).to.eq(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__italic').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('下划线', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].underline).to.eq(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__underline').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('删除线', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].strikeout).to.eq(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__strikeout').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('上标', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].type).to.eq('superscript')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__superscript').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('下标', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].type).to.eq('subscript')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
cy.get('.menu-item__subscript').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('字体颜色', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].color).to.eq('red')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
editor.command.executeColor('red')
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('高亮', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const data = payload.data
|
||||||
|
|
||||||
|
expect(data[0].highlight).to.eq('red')
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
editor.command.executeInsertElementList([{
|
||||||
|
value: text
|
||||||
|
}])
|
||||||
|
|
||||||
|
editor.command.executeSetRange(0, textLength)
|
||||||
|
|
||||||
|
editor.command.executeHighlight('red')
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-撤销&重做', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
|
||||||
|
it('撤销', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
expect(payload.data[0].value).to.eq(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
cy.get('@canvas').type(`${text}1`)
|
||||||
|
|
||||||
|
cy.get('.menu-item__undo').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type(`{ctrl}s`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('重做', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
expect(payload.data[0].value).to.eq(`${text}1`)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.command.executeSelectAll()
|
||||||
|
|
||||||
|
editor.command.executeBackspace()
|
||||||
|
|
||||||
|
cy.get('@canvas').type(`${text}1`)
|
||||||
|
|
||||||
|
cy.get('.menu-item__undo').click()
|
||||||
|
|
||||||
|
cy.get('.menu-item__redo').click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type(`{ctrl}s`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import Editor from '../../../src/editor'
|
||||||
|
|
||||||
|
describe('菜单-水印', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/canvas-editor/')
|
||||||
|
|
||||||
|
cy.get('canvas').first().as('canvas').should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
const text = 'canvas-editor'
|
||||||
|
const size = 80
|
||||||
|
|
||||||
|
it('添加水印', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const { watermark } = payload
|
||||||
|
|
||||||
|
expect(watermark?.data).to.eq(text)
|
||||||
|
|
||||||
|
expect(watermark?.size).to.eq(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.get('.menu-item__watermark').click()
|
||||||
|
|
||||||
|
cy.get('.menu-item__watermark li').eq(0).click()
|
||||||
|
|
||||||
|
cy.get('.dialog-option [name="data"]').type(text)
|
||||||
|
|
||||||
|
cy.get('.dialog-option [name="size"]').as('size')
|
||||||
|
|
||||||
|
cy.get('@size').clear()
|
||||||
|
|
||||||
|
cy.get('@size').type(`${size}`)
|
||||||
|
|
||||||
|
cy.get('.dialog-menu button').eq(1).click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('删除水印', () => {
|
||||||
|
cy.getEditor().then((editor: Editor) => {
|
||||||
|
editor.listener.saved = function (payload) {
|
||||||
|
const { watermark } = payload
|
||||||
|
|
||||||
|
expect(watermark?.data).to.eq(undefined)
|
||||||
|
|
||||||
|
expect(watermark?.size).to.eq(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.get('.menu-item__watermark').click()
|
||||||
|
|
||||||
|
cy.get('.menu-item__watermark li').eq(1).click()
|
||||||
|
|
||||||
|
cy.get('@canvas').type('{ctrl}s')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Cypress.PluginConfig}
|
||||||
|
*/
|
||||||
|
export default (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {
|
||||||
|
// `on` is used to hook into various events Cypress emits
|
||||||
|
// `config` is the resolved Cypress config
|
||||||
|
// codeCoverageTask(on, config)
|
||||||
|
return config
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import 'cypress-file-upload'
|
||||||
|
|
||||||
|
Cypress.Commands.add('getEditor', () => {
|
||||||
|
return cy.window().its('editor')
|
||||||
|
})
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import './commands'
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es2015", "dom", "esnext"],
|
||||||
|
"types": ["cypress", "cypress-file-upload"],
|
||||||
|
"isolatedModules": false,
|
||||||
|
"allowJs": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
+5
-1
@@ -4,11 +4,15 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"lib": "tsc && vite build --mode lib",
|
"lib": "tsc && vite build --mode lib",
|
||||||
"build": "tsc && vite build --mode app",
|
"build": "tsc && vite build --mode app",
|
||||||
"serve": "vite preview"
|
"serve": "vite preview",
|
||||||
|
"cypress:open": "cypress open",
|
||||||
|
"cypress:run": "cypress run"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.11.12",
|
"@types/node": "^16.11.12",
|
||||||
"@types/prismjs": "^1.26.0",
|
"@types/prismjs": "^1.26.0",
|
||||||
|
"cypress": "^9.5.1",
|
||||||
|
"cypress-file-upload": "^5.0.8",
|
||||||
"typescript": "^4.3.2",
|
"typescript": "^4.3.2",
|
||||||
"vite": "^2.4.2"
|
"vite": "^2.4.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ export class Command {
|
|||||||
private static copy: Function
|
private static copy: Function
|
||||||
private static paste: Function
|
private static paste: Function
|
||||||
private static selectAll: Function
|
private static selectAll: Function
|
||||||
|
private static backspace: Function
|
||||||
|
private static setRange: Function
|
||||||
private static undo: Function
|
private static undo: Function
|
||||||
private static redo: Function
|
private static redo: Function
|
||||||
private static painter: Function
|
private static painter: Function
|
||||||
|
private static applyPainterStyle: Function
|
||||||
private static format: Function
|
private static format: Function
|
||||||
private static font: Function
|
private static font: Function
|
||||||
private static sizeAdd: Function
|
private static sizeAdd: Function
|
||||||
@@ -60,9 +63,12 @@ export class Command {
|
|||||||
Command.copy = adapt.copy.bind(adapt)
|
Command.copy = adapt.copy.bind(adapt)
|
||||||
Command.paste = adapt.paste.bind(adapt)
|
Command.paste = adapt.paste.bind(adapt)
|
||||||
Command.selectAll = adapt.selectAll.bind(adapt)
|
Command.selectAll = adapt.selectAll.bind(adapt)
|
||||||
|
Command.backspace = adapt.backspace.bind(adapt)
|
||||||
|
Command.setRange = adapt.setRange.bind(adapt)
|
||||||
Command.undo = adapt.undo.bind(adapt)
|
Command.undo = adapt.undo.bind(adapt)
|
||||||
Command.redo = adapt.redo.bind(adapt)
|
Command.redo = adapt.redo.bind(adapt)
|
||||||
Command.painter = adapt.painter.bind(adapt)
|
Command.painter = adapt.painter.bind(adapt)
|
||||||
|
Command.applyPainterStyle = adapt.applyPainterStyle.bind(adapt)
|
||||||
Command.format = adapt.format.bind(adapt)
|
Command.format = adapt.format.bind(adapt)
|
||||||
Command.font = adapt.font.bind(adapt)
|
Command.font = adapt.font.bind(adapt)
|
||||||
Command.sizeAdd = adapt.sizeAdd.bind(adapt)
|
Command.sizeAdd = adapt.sizeAdd.bind(adapt)
|
||||||
@@ -124,6 +130,14 @@ export class Command {
|
|||||||
return Command.selectAll()
|
return Command.selectAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public executeBackspace() {
|
||||||
|
return Command.backspace()
|
||||||
|
}
|
||||||
|
|
||||||
|
public executeSetRange(startIndex: number, endIndex: number) {
|
||||||
|
return Command.setRange(startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
// 撤销、重做、格式刷、清除格式
|
// 撤销、重做、格式刷、清除格式
|
||||||
public executeUndo() {
|
public executeUndo() {
|
||||||
return Command.undo()
|
return Command.undo()
|
||||||
@@ -137,6 +151,10 @@ export class Command {
|
|||||||
return Command.painter()
|
return Command.painter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public executeApplyPainterStyle() {
|
||||||
|
return Command.applyPainterStyle()
|
||||||
|
}
|
||||||
|
|
||||||
public executeFormat() {
|
public executeFormat() {
|
||||||
return Command.format()
|
return Command.format()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,38 @@ export class CommandAdapt {
|
|||||||
this.canvasEvent.selectAll()
|
this.canvasEvent.selectAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public backspace() {
|
||||||
|
const isReadonly = this.draw.isReadonly()
|
||||||
|
if (isReadonly) return
|
||||||
|
const elementList = this.draw.getElementList()
|
||||||
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
|
const isCollapsed = startIndex === endIndex
|
||||||
|
// 首字符禁止删除
|
||||||
|
if (isCollapsed && elementList[startIndex].value === ZERO && startIndex === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!isCollapsed) {
|
||||||
|
elementList.splice(startIndex + 1, endIndex - startIndex)
|
||||||
|
} else {
|
||||||
|
elementList.splice(startIndex, 1)
|
||||||
|
}
|
||||||
|
const curIndex = isCollapsed ? startIndex - 1 : startIndex
|
||||||
|
this.range.setRange(curIndex, curIndex)
|
||||||
|
this.draw.render({ curIndex })
|
||||||
|
}
|
||||||
|
|
||||||
|
public setRange(startIndex: number, endIndex: number) {
|
||||||
|
if (startIndex < 0 || endIndex < 0 || endIndex < startIndex) return
|
||||||
|
this.range.setRange(startIndex, endIndex)
|
||||||
|
const isCollapsed = startIndex === endIndex
|
||||||
|
this.draw.render({
|
||||||
|
curIndex: isCollapsed ? startIndex : undefined,
|
||||||
|
isComputeRowList: false,
|
||||||
|
isSubmitHistory: false,
|
||||||
|
isSetCursor: isCollapsed
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public undo() {
|
public undo() {
|
||||||
const isReadonly = this.draw.isReadonly()
|
const isReadonly = this.draw.isReadonly()
|
||||||
if (isReadonly) return
|
if (isReadonly) return
|
||||||
@@ -108,6 +140,10 @@ export class CommandAdapt {
|
|||||||
this.draw.setPainterStyle(painterStyle)
|
this.draw.setPainterStyle(painterStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public applyPainterStyle() {
|
||||||
|
this.canvasEvent.applyPainterStyle()
|
||||||
|
}
|
||||||
|
|
||||||
public format() {
|
public format() {
|
||||||
const isReadonly = this.draw.isReadonly()
|
const isReadonly = this.draw.isReadonly()
|
||||||
if (isReadonly) return
|
if (isReadonly) return
|
||||||
|
|||||||
@@ -68,11 +68,15 @@ export class CanvasEvent {
|
|||||||
|
|
||||||
public setIsAllowDrag(payload: boolean) {
|
public setIsAllowDrag(payload: boolean) {
|
||||||
this.isAllowDrag = payload
|
this.isAllowDrag = payload
|
||||||
if (payload === false) {
|
if (!payload) {
|
||||||
|
this.applyPainterStyle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public applyPainterStyle() {
|
||||||
this.pageList.forEach(p => {
|
this.pageList.forEach(p => {
|
||||||
p.style.cursor = 'text'
|
p.style.cursor = 'text'
|
||||||
})
|
})
|
||||||
// 应用格式刷样式
|
|
||||||
const painterStyle = this.draw.getPainterStyle()
|
const painterStyle = this.draw.getPainterStyle()
|
||||||
if (!painterStyle) return
|
if (!painterStyle) return
|
||||||
const selection = this.range.getSelection()
|
const selection = this.range.getSelection()
|
||||||
@@ -87,7 +91,6 @@ export class CanvasEvent {
|
|||||||
this.draw.setPainterStyle(null)
|
this.draw.setPainterStyle(null)
|
||||||
this.draw.render({ isSetCursor: false })
|
this.draw.render({ isSetCursor: false })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public mousemove(evt: MouseEvent) {
|
public mousemove(evt: MouseEvent) {
|
||||||
if (!this.isAllowDrag || !this.mouseDownStartPosition) return
|
if (!this.isAllowDrag || !this.mouseDownStartPosition) return
|
||||||
@@ -246,7 +249,7 @@ export class CanvasEvent {
|
|||||||
if (evt.key === KeyMap.Backspace) {
|
if (evt.key === KeyMap.Backspace) {
|
||||||
if (isReadonly) return
|
if (isReadonly) return
|
||||||
// 判断是否允许删除
|
// 判断是否允许删除
|
||||||
if (elementList[index].value === ZERO && index === 0) {
|
if (isCollapsed && elementList[index].value === ZERO && index === 0) {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array<keyof IElement> = [
|
|||||||
'underline',
|
'underline',
|
||||||
'strikeout',
|
'strikeout',
|
||||||
'rowFlex',
|
'rowFlex',
|
||||||
|
'rowMargin',
|
||||||
|
'dashArray',
|
||||||
'trList',
|
'trList',
|
||||||
'width',
|
'width',
|
||||||
'height',
|
'height',
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ window.onload = function () {
|
|||||||
const container = document.querySelector<HTMLDivElement>('.editor')!
|
const container = document.querySelector<HTMLDivElement>('.editor')!
|
||||||
const instance = new Editor(container, <IElement[]>data, options)
|
const instance = new Editor(container, <IElement[]>data, options)
|
||||||
console.log('实例: ', instance)
|
console.log('实例: ', instance)
|
||||||
|
// cypress使用
|
||||||
|
Reflect.set(window, 'editor', instance)
|
||||||
|
|
||||||
// 2. | 撤销 | 重做 | 格式刷 | 清除格式 |
|
// 2. | 撤销 | 重做 | 格式刷 | 清除格式 |
|
||||||
const undoDom = document.querySelector<HTMLDivElement>('.menu-item__undo')!
|
const undoDom = document.querySelector<HTMLDivElement>('.menu-item__undo')!
|
||||||
|
|||||||
Reference in New Issue
Block a user