返回 oh-my-ppt
edit-mode-ref-lines.test.ts
根目录 / tests / unit / preview / edit-mode-ref-lines.test.ts
1 import { Window } from 'happy-dom'
2 import { describe, expect, it } from 'vitest'
3 import {
4 EDIT_MODE_CONSOLE_PREFIX,
5 buildEditModeInjectScript
6 } from '@arcsin1/presentation-editor-runtime'
7
8 type Rect = Pick<DOMRect, 'left' | 'top' | 'right' | 'bottom' | 'width' | 'height'>
9
10 const rect = (left: number, top: number, width: number, height: number): Rect => ({
11 left,
12 top,
13 right: left + width,
14 bottom: top + height,
15 width,
16 height
17 })
18
19 describe('preview edit mode reference lines', () => {
20 it('supports element, permanent-guide, and grid snapping inside the webview guest', () => {
21 const window = new Window({ url: 'http://localhost/page.html' }) as unknown as Window & {
22 ResizeObserver: typeof ResizeObserver
23 eval: (code: string) => void
24 }
25 const { document } = window
26 const logs: string[] = []
27
28 document.body.setAttribute('data-page-id', 'page')
29 document.body.innerHTML = `
30 <main class="ppt-page-root" data-ppt-guard-root="1" data-ppt-width="1000" data-ppt-height="600">
31 <main data-block-id="content" data-role="content">
32 <div data-block-id="drag">Drag</div>
33 <div data-block-id="target">Target</div>
34 </main>
35 </main>
36 `
37
38 const root = document.querySelector('.ppt-page-root') as HTMLElement
39 const content = document.querySelector('[data-block-id="content"]') as HTMLElement
40 const drag = document.querySelector('[data-block-id="drag"]') as HTMLElement
41 const target = document.querySelector('[data-block-id="target"]') as HTMLElement
42
43 window.HTMLElement.prototype.getBoundingClientRect = function () {
44 if (this === root || this === content) return rect(0, 0, 1000, 600)
45 if (this === target) return rect(300, 100, 100, 100)
46 if (this === drag) {
47 const x = Number.parseFloat(drag.style.getPropertyValue('--ppt-drag-x')) || 0
48 const y = Number.parseFloat(drag.style.getPropertyValue('--ppt-drag-y')) || 0
49 return rect(100 + x, 100 + y, 100, 100)
50 }
51 return rect(0, 0, 0, 0)
52 }
53 window.HTMLElement.prototype.getClientRects = function () {
54 return [this.getBoundingClientRect()]
55 }
56
57 document.elementFromPoint = () => drag
58 document.elementsFromPoint = () => [drag, content, root]
59 window.ResizeObserver = class {
60 observe() {}
61 disconnect() {}
62 } as unknown as typeof ResizeObserver
63 window.console.log = (value?: unknown) => logs.push(String(value))
64
65 window.eval(buildEditModeInjectScript())
66 const editWindow = window as unknown as Window & {
67 __pptEditModeReadSnapPoints: () => { x: number[]; y: number[] }
68 __pptEditModeSetSnapSettings: (settings: unknown) => void
69 }
70 const dragFromTo = (startX: number, endX: number): void => {
71 drag.dispatchEvent(
72 new window.PointerEvent('pointerdown', {
73 bubbles: true,
74 cancelable: true,
75 button: 0,
76 clientX: startX,
77 clientY: 110,
78 pointerId: 1
79 })
80 )
81 for (let index = 0; index < 2; index += 1) {
82 drag.dispatchEvent(
83 new window.PointerEvent('pointermove', {
84 bubbles: true,
85 cancelable: true,
86 clientX: endX,
87 clientY: 110,
88 pointerId: 1
89 })
90 )
91 }
92 drag.dispatchEvent(
93 new window.PointerEvent('pointerup', {
94 bubbles: true,
95 cancelable: true,
96 button: 0,
97 clientX: endX,
98 clientY: 110,
99 pointerId: 1
100 })
101 )
102 }
103
104 expect(editWindow.__pptEditModeReadSnapPoints().x).toEqual(
105 expect.arrayContaining([300, 350, 400])
106 )
107 dragFromTo(110, 306)
108
109 expect(drag.style.getPropertyValue('--ppt-drag-x')).toBe('200.0px')
110 expect(drag.classList.contains('arcsin1-presentation-editor-selected')).toBe(false)
111 expect(document.querySelector('[data-arcsin1-presentation-editor-guide="vertical"]')).not.toBeNull()
112 expect(
113 (document.querySelector('[data-arcsin1-presentation-editor-guide="vertical"]') as HTMLElement).style.display
114 ).toBe('none')
115
116 const movedLog = logs.findLast((item) =>
117 item.startsWith(`${EDIT_MODE_CONSOLE_PREFIX}{"type":"moved"`)
118 )
119 expect(movedLog).toBeTruthy()
120 expect(JSON.parse(String(movedLog).slice(EDIT_MODE_CONSOLE_PREFIX.length))).toMatchObject({
121 selector: 'body[data-page-id="page"] [data-block-id="drag"]',
122 x: 200,
123 deltaX: 200,
124 visualX: 300
125 })
126
127 editWindow.__pptEditModeSetSnapSettings({
128 enabled: true,
129 guides: { vertical: [500], horizontal: [] },
130 grid: { enabled: false, size: 20 }
131 })
132 dragFromTo(310, 506)
133 expect(drag.style.getPropertyValue('--ppt-drag-x')).toBe('400.0px')
134
135 editWindow.__pptEditModeSetSnapSettings({
136 enabled: true,
137 guides: { vertical: [], horizontal: [] },
138 grid: { enabled: true, size: 100 }
139 })
140 dragFromTo(510, 607)
141 expect(drag.style.getPropertyValue('--ppt-drag-x')).toBe('500.0px')
142 })
143
144 it('fails loudly when the page root is missing slide size metadata', () => {
145 const window = new Window({ url: 'http://localhost/page.html' }) as unknown as Window & {
146 ResizeObserver: typeof ResizeObserver
147 eval: (code: string) => void
148 }
149 const { document } = window
150
151 document.body.setAttribute('data-page-id', 'page')
152 document.body.innerHTML = `
153 <main class="ppt-page-root" data-ppt-guard-root="1">
154 <main data-block-id="content" data-role="content">
155 <div data-block-id="target">Target</div>
156 </main>
157 </main>
158 `
159
160 window.HTMLElement.prototype.getBoundingClientRect = function () {
161 return rect(0, 0, 1000, 600)
162 }
163 window.HTMLElement.prototype.getClientRects = function () {
164 return [this.getBoundingClientRect()]
165 }
166 window.ResizeObserver = class {
167 observe() {}
168 disconnect() {}
169 } as unknown as typeof ResizeObserver
170
171 window.eval(buildEditModeInjectScript())
172 const editWindow = window as unknown as Window & {
173 __pptEditModeReadSnapPoints: () => { x: number[]; y: number[] }
174 }
175
176 expect(() => editWindow.__pptEditModeReadSnapPoints()).toThrow(
177 'missing page root slide size metadata'
178 )
179 })
180 })
181
181 lines TYPESCRIPT