ep_headings2 0.2.109 → 0.2.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"description": "Adds heading support to Etherpad Lite. Includes improved suppot for export, i18n etc.",
|
|
3
3
|
"name": "ep_headings2",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.111",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "John McLear",
|
|
7
7
|
"email": "john@mclear.co.uk"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"url": "https://github.com/ether/ep_headings2/issues"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"ep_plugin_helpers": "^0.2.
|
|
37
|
+
"ep_plugin_helpers": "^0.2.8"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"eslint": "^8.57.1",
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {expect, test} from '@playwright/test';
|
|
2
|
+
import {clearPadContent, getPadBody, goToNewPad, writeToPad}
|
|
3
|
+
from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';
|
|
4
|
+
|
|
5
|
+
test.beforeEach(async ({page}) => {
|
|
6
|
+
await goToNewPad(page);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test.describe('ep_headings2 - Set Heading and ensure its removed properly', () => {
|
|
10
|
+
test('Heading select has aria-label for accessibility', async ({page}) => {
|
|
11
|
+
const select = page.locator('#heading-selection');
|
|
12
|
+
await expect(select).toHaveAttribute('aria-label', /.+/);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('Focus returns to editor after selecting a heading', async ({page}) => {
|
|
16
|
+
const padBody = await getPadBody(page);
|
|
17
|
+
await padBody.click();
|
|
18
|
+
await clearPadContent(page);
|
|
19
|
+
await writeToPad(page, 'Test focus');
|
|
20
|
+
|
|
21
|
+
// Pick H1 in the heading-selection <select>; the niceSelect wrapper
|
|
22
|
+
// intercepts native change events so we set the value on the
|
|
23
|
+
// underlying element and dispatch change manually, matching what
|
|
24
|
+
// the legacy spec did with chrome$('#heading-selection').change().
|
|
25
|
+
await page.evaluate(() => {
|
|
26
|
+
const sel = document.querySelector<HTMLSelectElement>('#heading-selection')!;
|
|
27
|
+
sel.value = '0';
|
|
28
|
+
sel.dispatchEvent(new Event('change', {bubbles: true}));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// The line gains an <h1> wrapper.
|
|
32
|
+
await expect(padBody.locator('div').first().locator('h1')).toHaveCount(1);
|
|
33
|
+
|
|
34
|
+
// Focus should return to the inner ace iframe (not the toolbar
|
|
35
|
+
// <select> the user just clicked). Verify by checking activeElement
|
|
36
|
+
// in the outer document is the ace_inner iframe.
|
|
37
|
+
const focusedFrameName = await page.evaluate(() => {
|
|
38
|
+
const ae = document.activeElement as HTMLElement | null;
|
|
39
|
+
return (ae?.tagName === 'IFRAME' && (ae as HTMLIFrameElement).name) || null;
|
|
40
|
+
});
|
|
41
|
+
expect(focusedFrameName).toBe('ace_outer');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('Option select is changed when heading is changed', async ({page}) => {
|
|
45
|
+
const padBody = await getPadBody(page);
|
|
46
|
+
await padBody.click();
|
|
47
|
+
await clearPadContent(page);
|
|
48
|
+
await writeToPad(page, 'First Line!');
|
|
49
|
+
|
|
50
|
+
// Apply H1 to the first line.
|
|
51
|
+
await page.evaluate(() => {
|
|
52
|
+
const sel = document.querySelector<HTMLSelectElement>('#heading-selection')!;
|
|
53
|
+
sel.value = '0';
|
|
54
|
+
sel.dispatchEvent(new Event('change', {bubbles: true}));
|
|
55
|
+
});
|
|
56
|
+
await expect(padBody.locator('div').first().locator('h1')).toHaveCount(1);
|
|
57
|
+
|
|
58
|
+
// Move to a fresh second line — the selector should reset to
|
|
59
|
+
// "no heading" (-1) because the cursor is no longer on an H1.
|
|
60
|
+
await page.keyboard.press('End');
|
|
61
|
+
await page.keyboard.press('Enter');
|
|
62
|
+
await page.keyboard.type('Second Line');
|
|
63
|
+
|
|
64
|
+
// Wait for the selector to update to "-1" (Etherpad polls the
|
|
65
|
+
// current line's heading attribute on caret moves).
|
|
66
|
+
await expect.poll(async () =>
|
|
67
|
+
page.evaluate(() =>
|
|
68
|
+
(document.querySelector<HTMLSelectElement>('#heading-selection')!).value),
|
|
69
|
+
{timeout: 10_000}).toBe('-1');
|
|
70
|
+
|
|
71
|
+
// Second line is plain text, not wrapped in <h1>.
|
|
72
|
+
const second = padBody.locator('div').nth(1);
|
|
73
|
+
await expect(second.locator('h1')).toHaveCount(0);
|
|
74
|
+
await expect(second).toHaveText('Second Line');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
describe('ep_headings2 - Set Heading and ensure its removed properly', function () {
|
|
4
|
-
// create a new pad before each test run
|
|
5
|
-
beforeEach(function (cb) {
|
|
6
|
-
helper.newPad(cb);
|
|
7
|
-
this.timeout(60000);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
// Create Pad
|
|
11
|
-
// Check Default Text has no Heading
|
|
12
|
-
// Set Line 1 heading and check it's set
|
|
13
|
-
// Set Line 2 to null heading value and check it's set
|
|
14
|
-
|
|
15
|
-
it('Heading select has aria-label for accessibility', async function () {
|
|
16
|
-
this.timeout(60000);
|
|
17
|
-
const chrome$ = helper.padChrome$;
|
|
18
|
-
const $select = chrome$('#heading-selection');
|
|
19
|
-
expect($select.attr('aria-label')).to.not.be(undefined);
|
|
20
|
-
expect($select.attr('aria-label').length).to.be.greaterThan(0);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('Focus returns to editor after selecting a heading', async function () {
|
|
24
|
-
this.timeout(60000);
|
|
25
|
-
const chrome$ = helper.padChrome$;
|
|
26
|
-
const inner$ = helper.padInner$;
|
|
27
|
-
|
|
28
|
-
// Type some text
|
|
29
|
-
const $firstTextElement = inner$('div').first();
|
|
30
|
-
$firstTextElement.sendkeys('{selectall}');
|
|
31
|
-
$firstTextElement.sendkeys('Test focus');
|
|
32
|
-
|
|
33
|
-
// Select heading 1
|
|
34
|
-
chrome$('#heading-selection').val('0');
|
|
35
|
-
chrome$('#heading-selection').change();
|
|
36
|
-
|
|
37
|
-
// Wait for heading to be applied, then check focus is back in the editor
|
|
38
|
-
await helper.waitForPromise(() => inner$('div').first().find('h1').length === 1);
|
|
39
|
-
|
|
40
|
-
// The editor iframe should have focus, not the toolbar
|
|
41
|
-
const editorHasFocus = inner$('div').first().is(':focus') ||
|
|
42
|
-
inner$.document.hasFocus() ||
|
|
43
|
-
$(helper.padOuter$.document).find('iframe[name="ace_inner"]').is(':focus');
|
|
44
|
-
expect(editorHasFocus).to.be(true);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('Option select is changed when heading is changed', async function () {
|
|
48
|
-
this.timeout(60000);
|
|
49
|
-
const chrome$ = helper.padChrome$;
|
|
50
|
-
const inner$ = helper.padInner$;
|
|
51
|
-
|
|
52
|
-
const $firstTextElement = inner$('div').first();
|
|
53
|
-
|
|
54
|
-
const $editorContents = inner$('div');
|
|
55
|
-
$editorContents.sendkeys('{selectall}');
|
|
56
|
-
$firstTextElement.sendkeys('First Line!');
|
|
57
|
-
|
|
58
|
-
// sets first line to h1
|
|
59
|
-
chrome$('#heading-selection').val('0');
|
|
60
|
-
chrome$('#heading-selection').change();
|
|
61
|
-
|
|
62
|
-
$firstTextElement.sendkeys('{enter}');
|
|
63
|
-
|
|
64
|
-
await helper.waitForPromise(() => chrome$('#heading-selection').val() === '0');
|
|
65
|
-
inner$('div').first().sendkeys('{selectall}');
|
|
66
|
-
const $secondElement = inner$('div').first().next();
|
|
67
|
-
$secondElement.sendkeys('Second Line');
|
|
68
|
-
$secondElement.sendkeys('{selectall}');
|
|
69
|
-
await helper.waitForPromise(() => chrome$('#heading-selection').val() === '-1');
|
|
70
|
-
expect($secondElement.find('h1').length).to.be(0);
|
|
71
|
-
expect($secondElement.text()).to.be('Second Line');
|
|
72
|
-
});
|
|
73
|
-
});
|