@atlaskit/editor-common 69.1.2 → 69.2.2
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/CHANGELOG.md +31 -0
- package/dist/cjs/__tests_external__/cases/fundamentals/collection.js +21 -0
- package/dist/cjs/__tests_external__/cases/fundamentals/test-cases/editor-is-present.js +25 -0
- package/dist/cjs/__tests_external__/cases/fundamentals/test-cases/editor-typing.js +37 -0
- package/dist/cjs/__tests_external__/cases/media/test-cases/alt-text.js +45 -0
- package/dist/cjs/__tests_external__/cases/media/test-cases/caption.js +44 -0
- package/dist/cjs/__tests_external__/cases/media/test-cases/index.js +23 -0
- package/dist/cjs/__tests_external__/cases/media/test-cases/types.js +5 -0
- package/dist/cjs/__tests_external__/cases/media/test-cases/upload.js +38 -0
- package/dist/cjs/__tests_external__/cases/smart-links/index.js +37 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/block/delete.js +43 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/block/edit.js +77 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/block/index.js +23 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/block/insert.js +42 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/embed/delete.js +43 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/embed/edit.js +77 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/embed/index.js +23 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/embed/insert.js +42 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/inline/delete.js +42 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/inline/edit.js +75 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/inline/index.js +25 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/inline/insert.js +41 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/inline/unlink.js +44 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/types.js +5 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/url/index.js +19 -0
- package/dist/cjs/__tests_external__/cases/smart-links/test-cases/url/insert.js +39 -0
- package/dist/cjs/__tests_external__/cases/types.js +5 -0
- package/dist/cjs/__tests_external__/index.js +55 -0
- package/dist/cjs/__tests_external__/page-objects/Editor.js +71 -0
- package/dist/cjs/__tests_external__/page-objects/EditorMedia.js +98 -0
- package/dist/cjs/__tests_external__/page-objects/EditorSmartLink.js +232 -0
- package/dist/cjs/__tests_external__/page-objects/Renderer.js +59 -0
- package/dist/cjs/messages/codeBlockCopyButton.js +22 -0
- package/dist/cjs/messages/index.js +9 -1
- package/dist/cjs/styles/shared/block-marks.js +1 -1
- package/dist/cjs/styles/shared/code-block.js +4 -2
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/__tests_external__/cases/fundamentals/collection.js +7 -0
- package/dist/es2019/__tests_external__/cases/fundamentals/test-cases/editor-is-present.js +12 -0
- package/dist/es2019/__tests_external__/cases/fundamentals/test-cases/editor-typing.js +25 -0
- package/dist/es2019/__tests_external__/cases/media/test-cases/alt-text.js +29 -0
- package/dist/es2019/__tests_external__/cases/media/test-cases/caption.js +28 -0
- package/dist/es2019/__tests_external__/cases/media/test-cases/index.js +8 -0
- package/dist/es2019/__tests_external__/cases/media/test-cases/types.js +1 -0
- package/dist/es2019/__tests_external__/cases/media/test-cases/upload.js +23 -0
- package/dist/es2019/__tests_external__/cases/smart-links/index.js +4 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/block/delete.js +29 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/block/edit.js +59 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/block/index.js +8 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/block/insert.js +28 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/embed/delete.js +29 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/embed/edit.js +59 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/embed/index.js +8 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/embed/insert.js +28 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/inline/delete.js +28 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/inline/edit.js +57 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/inline/index.js +9 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/inline/insert.js +27 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/inline/unlink.js +30 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/types.js +1 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/url/index.js +6 -0
- package/dist/es2019/__tests_external__/cases/smart-links/test-cases/url/insert.js +25 -0
- package/dist/es2019/__tests_external__/cases/types.js +1 -0
- package/dist/es2019/__tests_external__/index.js +4 -0
- package/dist/es2019/__tests_external__/page-objects/Editor.js +29 -0
- package/dist/es2019/__tests_external__/page-objects/EditorMedia.js +51 -0
- package/dist/es2019/__tests_external__/page-objects/EditorSmartLink.js +168 -0
- package/dist/es2019/__tests_external__/page-objects/Renderer.js +17 -0
- package/dist/es2019/messages/codeBlockCopyButton.js +13 -0
- package/dist/es2019/messages/index.js +2 -1
- package/dist/es2019/styles/shared/block-marks.js +4 -2
- package/dist/es2019/styles/shared/code-block.js +18 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/__tests_external__/cases/fundamentals/collection.js +9 -0
- package/dist/esm/__tests_external__/cases/fundamentals/test-cases/editor-is-present.js +14 -0
- package/dist/esm/__tests_external__/cases/fundamentals/test-cases/editor-typing.js +25 -0
- package/dist/esm/__tests_external__/cases/media/test-cases/alt-text.js +32 -0
- package/dist/esm/__tests_external__/cases/media/test-cases/caption.js +31 -0
- package/dist/esm/__tests_external__/cases/media/test-cases/index.js +10 -0
- package/dist/esm/__tests_external__/cases/media/test-cases/types.js +1 -0
- package/dist/esm/__tests_external__/cases/media/test-cases/upload.js +26 -0
- package/dist/esm/__tests_external__/cases/smart-links/index.js +4 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/block/delete.js +30 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/block/edit.js +61 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/block/index.js +10 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/block/insert.js +29 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/embed/delete.js +30 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/embed/edit.js +61 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/embed/index.js +10 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/embed/insert.js +29 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/inline/delete.js +29 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/inline/edit.js +59 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/inline/index.js +11 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/inline/insert.js +28 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/inline/unlink.js +31 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/types.js +1 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/url/index.js +8 -0
- package/dist/esm/__tests_external__/cases/smart-links/test-cases/url/insert.js +26 -0
- package/dist/esm/__tests_external__/cases/types.js +1 -0
- package/dist/esm/__tests_external__/index.js +4 -0
- package/dist/esm/__tests_external__/page-objects/Editor.js +56 -0
- package/dist/esm/__tests_external__/page-objects/EditorMedia.js +83 -0
- package/dist/esm/__tests_external__/page-objects/EditorSmartLink.js +218 -0
- package/dist/esm/__tests_external__/page-objects/Renderer.js +45 -0
- package/dist/esm/messages/codeBlockCopyButton.js +13 -0
- package/dist/esm/messages/index.js +2 -1
- package/dist/esm/styles/shared/block-marks.js +1 -1
- package/dist/esm/styles/shared/code-block.js +4 -2
- package/dist/esm/version.json +1 -1
- package/dist/types/messages/codeBlockCopyButton.d.ts +12 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/styles/shared/code-block.d.ts +2 -0
- package/package.json +10 -11
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { blockSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const switchToBlockSmartLinkTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'switching to a Block Smart Link works',
|
|
10
|
+
id: 'switch-to-block',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
15
|
+
editorWithSmartLinks.switchAfterInsert('block');
|
|
16
|
+
blockSmartLinkRendersTestCase({
|
|
17
|
+
url
|
|
18
|
+
}).assertions(cy);
|
|
19
|
+
|
|
20
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
21
|
+
const renderer = editor.publish(ui);
|
|
22
|
+
renderer.expectContentReady();
|
|
23
|
+
blockSmartLinkRendersTestCase({
|
|
24
|
+
url
|
|
25
|
+
}).assertions(cy);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { EmbedCardPageObject, embedSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const deleteEmbedSmartLinkTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'able to delete an Embed Smart Link using floating toolbar',
|
|
10
|
+
id: 'delete-embed-smart-link',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
const embedCard = new EmbedCardPageObject(cy);
|
|
15
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
16
|
+
editorWithSmartLinks.switchAfterInsert('embed');
|
|
17
|
+
embedSmartLinkRendersTestCase({
|
|
18
|
+
url
|
|
19
|
+
}).assertions(cy);
|
|
20
|
+
editorWithSmartLinks.deleteSmartLink('embed');
|
|
21
|
+
embedCard.expectCardNotExists();
|
|
22
|
+
|
|
23
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
24
|
+
const renderer = editor.publish(ui);
|
|
25
|
+
renderer.expectContentReady();
|
|
26
|
+
embedCard.expectCardNotExists();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { EmbedCardPageObject, embedSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const editEmbedSmartLinkTitleTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'editing an Embed Smart Link renders as Blue Link (title)',
|
|
10
|
+
id: 'embed-smart-link-edit-title',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
const embedCard = new EmbedCardPageObject(cy);
|
|
15
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
16
|
+
editorWithSmartLinks.switchAfterInsert('embed');
|
|
17
|
+
embedSmartLinkRendersTestCase({
|
|
18
|
+
url
|
|
19
|
+
}).assertions(cy);
|
|
20
|
+
editorWithSmartLinks.changeLinkLabel('Avocorne is watching you 🥑', 'embed');
|
|
21
|
+
embedCard.assertHrefRendered(url);
|
|
22
|
+
embedCard.expectCardNotExists();
|
|
23
|
+
|
|
24
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
25
|
+
const renderer = editor.publish(ui);
|
|
26
|
+
renderer.expectContentReady();
|
|
27
|
+
embedCard.assertHrefRendered(url);
|
|
28
|
+
embedCard.expectCardNotExists();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
export const editEmbedSmartLinkUrlTestCase = ({
|
|
33
|
+
ui,
|
|
34
|
+
url
|
|
35
|
+
}) => new InProductTestCase({
|
|
36
|
+
title: 'editing an Embed Smart Link renders as Blue Link (URL)',
|
|
37
|
+
id: 'embed-smart-link-edit',
|
|
38
|
+
assertions: cy => {
|
|
39
|
+
const editor = new EditorPageObject(cy);
|
|
40
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
41
|
+
const embedCard = new EmbedCardPageObject(cy);
|
|
42
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
43
|
+
editorWithSmartLinks.switchAfterInsert('embed');
|
|
44
|
+
embedSmartLinkRendersTestCase({
|
|
45
|
+
url
|
|
46
|
+
}).assertions(cy);
|
|
47
|
+
const newUrl = 'https://avo.corne.com';
|
|
48
|
+
editorWithSmartLinks.changeLinkUrl(newUrl, 'embed');
|
|
49
|
+
embedCard.expectCardNotExists();
|
|
50
|
+
embedCard.assertHrefRendered(newUrl);
|
|
51
|
+
|
|
52
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
53
|
+
const renderer = editor.publish(ui);
|
|
54
|
+
renderer.expectContentReady();
|
|
55
|
+
embedCard.assertHrefRendered(newUrl);
|
|
56
|
+
embedCard.expectCardNotExists();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { InProductTestCollection } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { deleteEmbedSmartLinkTestCase } from './delete';
|
|
3
|
+
import { editEmbedSmartLinkTitleTestCase, editEmbedSmartLinkUrlTestCase } from './edit';
|
|
4
|
+
import { switchToEmbedSmartLinkTestCase } from './insert';
|
|
5
|
+
export const embedSmartLinksTestCollection = opts => new InProductTestCollection({
|
|
6
|
+
title: '@atlaskit/editor-core -> Embed Smart Links, happy paths',
|
|
7
|
+
testCases: [switchToEmbedSmartLinkTestCase(opts), editEmbedSmartLinkTitleTestCase(opts), editEmbedSmartLinkUrlTestCase(opts), deleteEmbedSmartLinkTestCase(opts)]
|
|
8
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { embedSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const switchToEmbedSmartLinkTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'switching to an Embed Smart Link works',
|
|
10
|
+
id: 'switch-to-embed',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
15
|
+
editorWithSmartLinks.switchAfterInsert('embed');
|
|
16
|
+
embedSmartLinkRendersTestCase({
|
|
17
|
+
url
|
|
18
|
+
}).assertions(cy);
|
|
19
|
+
|
|
20
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
21
|
+
const renderer = editor.publish(ui);
|
|
22
|
+
renderer.expectContentReady();
|
|
23
|
+
embedSmartLinkRendersTestCase({
|
|
24
|
+
url
|
|
25
|
+
}).assertions(cy);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { InlineCardPageObject, inlineSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const deleteInlineSmartLinkTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'able to delete an Inline Smart Link using floating toolbar',
|
|
10
|
+
id: 'delete-inline-smart-link',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
const inlineCard = new InlineCardPageObject(cy);
|
|
15
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
16
|
+
inlineSmartLinkRendersTestCase({
|
|
17
|
+
url
|
|
18
|
+
}).assertions(cy);
|
|
19
|
+
editorWithSmartLinks.deleteSmartLink();
|
|
20
|
+
inlineCard.expectCardNotExists();
|
|
21
|
+
|
|
22
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
23
|
+
const renderer = editor.publish(ui);
|
|
24
|
+
renderer.expectContentReady();
|
|
25
|
+
inlineCard.expectCardNotExists();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { InlineCardPageObject, inlineSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const editInlineSmartLinkTitleTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'editing an Inline Smart Link renders as Blue Link (title)',
|
|
10
|
+
id: 'inline-smart-link-edit-title',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
const inlineCard = new InlineCardPageObject(cy);
|
|
15
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
16
|
+
inlineSmartLinkRendersTestCase({
|
|
17
|
+
url
|
|
18
|
+
}).assertions(cy);
|
|
19
|
+
editorWithSmartLinks.changeLinkLabel('Avocorne is watching you 🥑');
|
|
20
|
+
inlineCard.assertHrefRendered(url);
|
|
21
|
+
inlineCard.expectCardNotExists();
|
|
22
|
+
|
|
23
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
24
|
+
const renderer = editor.publish(ui);
|
|
25
|
+
renderer.expectContentReady();
|
|
26
|
+
inlineCard.assertHrefRendered(url);
|
|
27
|
+
inlineCard.expectCardNotExists();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
export const editInlineSmartLinkUrlTestCase = ({
|
|
32
|
+
ui,
|
|
33
|
+
url
|
|
34
|
+
}) => new InProductTestCase({
|
|
35
|
+
title: 'editing an Inline Smart Link renders as Blue Link (URL)',
|
|
36
|
+
id: 'inline-smart-link-edit',
|
|
37
|
+
assertions: cy => {
|
|
38
|
+
const editor = new EditorPageObject(cy);
|
|
39
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
40
|
+
const inlineCard = new InlineCardPageObject(cy);
|
|
41
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
42
|
+
inlineSmartLinkRendersTestCase({
|
|
43
|
+
url
|
|
44
|
+
}).assertions(cy);
|
|
45
|
+
const newUrl = 'https://avo.corne.com';
|
|
46
|
+
editorWithSmartLinks.changeLinkUrl(newUrl);
|
|
47
|
+
inlineCard.expectCardNotExists();
|
|
48
|
+
inlineCard.assertHrefRendered(newUrl);
|
|
49
|
+
|
|
50
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
51
|
+
const renderer = editor.publish(ui);
|
|
52
|
+
renderer.expectContentReady();
|
|
53
|
+
inlineCard.assertHrefRendered(newUrl);
|
|
54
|
+
inlineCard.expectCardNotExists();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InProductTestCollection } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { deleteInlineSmartLinkTestCase } from './delete';
|
|
3
|
+
import { editInlineSmartLinkTitleTestCase, editInlineSmartLinkUrlTestCase } from './edit';
|
|
4
|
+
import { insertInlineSmartLinkTestCase } from './insert';
|
|
5
|
+
import { unlinkInlineSmartLinkTestCase } from './unlink';
|
|
6
|
+
export const inlineSmartLinksTestCollection = opts => new InProductTestCollection({
|
|
7
|
+
title: '@atlaskit/editor-core -> Inline Smart Links, happy paths',
|
|
8
|
+
testCases: [insertInlineSmartLinkTestCase(opts), editInlineSmartLinkTitleTestCase(opts), editInlineSmartLinkUrlTestCase(opts), unlinkInlineSmartLinkTestCase(opts), deleteInlineSmartLinkTestCase(opts)]
|
|
9
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { inlineSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const insertInlineSmartLinkTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'inserting an Inline Smart Link works',
|
|
10
|
+
id: 'inline-smart-link-insert',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
15
|
+
inlineSmartLinkRendersTestCase({
|
|
16
|
+
url
|
|
17
|
+
}).assertions(cy);
|
|
18
|
+
|
|
19
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
20
|
+
const renderer = editor.publish(ui);
|
|
21
|
+
renderer.expectContentReady();
|
|
22
|
+
inlineSmartLinkRendersTestCase({
|
|
23
|
+
url
|
|
24
|
+
}).assertions(cy);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { InlineCardPageObject, inlineSmartLinkRendersTestCase } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const unlinkInlineSmartLinkTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'unlinking an Inline Smart Link works',
|
|
10
|
+
id: 'inline-smart-link-unlink',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
const inlineCard = new InlineCardPageObject(cy);
|
|
15
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
16
|
+
inlineSmartLinkRendersTestCase({
|
|
17
|
+
url
|
|
18
|
+
}).assertions(cy);
|
|
19
|
+
editorWithSmartLinks.unlink();
|
|
20
|
+
inlineCard.expectCardNotExists();
|
|
21
|
+
inlineCard.assertHrefNotRendered(url);
|
|
22
|
+
|
|
23
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
24
|
+
const renderer = editor.publish(ui);
|
|
25
|
+
renderer.expectContentReady();
|
|
26
|
+
inlineCard.expectCardNotExists();
|
|
27
|
+
inlineCard.assertHrefNotRendered(url);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { InProductTestCollection } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { switchToUrlTestCase } from './insert';
|
|
3
|
+
export const blueLinksTestCollection = opts => new InProductTestCollection({
|
|
4
|
+
title: '@atlaskit/editor-core -> Blue Links, happy paths',
|
|
5
|
+
testCases: [switchToUrlTestCase(opts)]
|
|
6
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { InProductTestCase } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { InlineCardPageObject } from '@atlaskit/smart-card/in-product';
|
|
3
|
+
import { EditorPageObject } from '../../../../page-objects/Editor';
|
|
4
|
+
import { EditorSmartLinkPageObject } from '../../../../page-objects/EditorSmartLink';
|
|
5
|
+
export const switchToUrlTestCase = ({
|
|
6
|
+
ui,
|
|
7
|
+
url
|
|
8
|
+
}) => new InProductTestCase({
|
|
9
|
+
title: 'switching to a Blue Link works',
|
|
10
|
+
id: 'switch-to-url',
|
|
11
|
+
assertions: cy => {
|
|
12
|
+
const editor = new EditorPageObject(cy);
|
|
13
|
+
const editorWithSmartLinks = new EditorSmartLinkPageObject(cy, editor);
|
|
14
|
+
const inlineCard = new InlineCardPageObject(cy);
|
|
15
|
+
editorWithSmartLinks.insertSmartLinkByTyping(url);
|
|
16
|
+
editorWithSmartLinks.switchAfterInsert('url');
|
|
17
|
+
inlineCard.assertHrefRendered(url);
|
|
18
|
+
|
|
19
|
+
if (ui !== null && ui !== void 0 && ui.publishButton) {
|
|
20
|
+
const renderer = editor.publish(ui);
|
|
21
|
+
renderer.expectContentReady();
|
|
22
|
+
inlineCard.assertHrefRendered(url);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { EditorPageObject } from './page-objects/Editor';
|
|
2
|
+
export { fundamentalsTestCollection } from './cases/fundamentals/collection';
|
|
3
|
+
export { inlineSmartLinksTestCollection, blockSmartLinksTestCollection, embedSmartLinksTestCollection, blueLinksTestCollection } from './cases/smart-links';
|
|
4
|
+
export { mediaTestCollection } from './cases/media/test-cases';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { InProductTestPageObject } from '@atlaskit/in-product-testing';
|
|
2
|
+
import { RendererPageObject } from './Renderer';
|
|
3
|
+
export class EditorPageObject extends InProductTestPageObject {
|
|
4
|
+
getEditorArea() {
|
|
5
|
+
return this.cy.get('div.ProseMirror', {
|
|
6
|
+
timeout: 30000
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getTitleArea() {
|
|
11
|
+
return this.cy.get('[data-test-id="editor-title"]');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
publish(opts) {
|
|
15
|
+
if (opts !== null && opts !== void 0 && opts.publishButton) {
|
|
16
|
+
this.cy.get(opts.publishButton).click();
|
|
17
|
+
return new RendererPageObject(this.cy);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
throw Error('No publish button selector supplied!');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
upload(fixtures) {
|
|
24
|
+
return this.getEditorArea().attachFile(fixtures, {
|
|
25
|
+
subjectType: 'drag-n-drop'
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { InProductTestPageObject } from '@atlaskit/in-product-testing';
|
|
3
|
+
import { MediaCardPageObject } from '@atlaskit/media-card/in-product';
|
|
4
|
+
export class EditorMediaPageObject extends InProductTestPageObject {
|
|
5
|
+
constructor(cy) {
|
|
6
|
+
super(cy);
|
|
7
|
+
|
|
8
|
+
_defineProperty(this, "testIds", {
|
|
9
|
+
altTextButton: 'alt-text-edit-button',
|
|
10
|
+
altTextInput: 'alt-text-input',
|
|
11
|
+
captionPlaceholder: 'caption-placeholder',
|
|
12
|
+
caption: 'media-caption'
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
this.cy = cy;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
addAltTextToImage(altText, cardNumber = 1) {
|
|
19
|
+
const mediaCard = new MediaCardPageObject(this.cy);
|
|
20
|
+
mediaCard.findCardNth(cardNumber).click();
|
|
21
|
+
this.getAltTextButton().click();
|
|
22
|
+
this.getAltTextInput().type(`${altText}{enter}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
addCaptionToImage(captionText, cardNumber = 1) {
|
|
26
|
+
const mediaCard = new MediaCardPageObject(this.cy);
|
|
27
|
+
mediaCard.findCardNth(cardNumber).click();
|
|
28
|
+
this.getCaptionInput().type(`${captionText}{enter}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
assertCaptionText(captionText) {
|
|
32
|
+
return this.cy.get(this.toTestId(this.testIds.caption)).should('have.text', captionText);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
assertAltText(altText) {
|
|
36
|
+
return this.cy.get('.mediaView-content-wrap').should('have.attr', 'alt', altText);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getAltTextButton() {
|
|
40
|
+
return this.cy.get(this.toTestId(this.testIds.altTextButton)).should('be.visible');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getAltTextInput() {
|
|
44
|
+
return this.cy.get(this.toTestId(this.testIds.altTextInput)).should('be.visible');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getCaptionInput() {
|
|
48
|
+
return this.cy.get(this.toTestId(this.testIds.captionPlaceholder)).should('be.visible');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { InProductTestPageObject } from '@atlaskit/in-product-testing';
|
|
3
|
+
import { BlockCardPageObject, EmbedCardPageObject, InlineCardPageObject } from '@atlaskit/smart-card/in-product';
|
|
4
|
+
export class EditorSmartLinkPageObject extends InProductTestPageObject {
|
|
5
|
+
constructor(cy, editor) {
|
|
6
|
+
super(cy);
|
|
7
|
+
|
|
8
|
+
_defineProperty(this, "testIds", {
|
|
9
|
+
viewSwitcher: 'link-toolbar-appearance-button',
|
|
10
|
+
displayUrlOption: 'url-appearance',
|
|
11
|
+
displayInlineOption: 'inline-appearance',
|
|
12
|
+
displayCardOption: 'block-appearance',
|
|
13
|
+
displayEmbedOption: 'embed-appearance',
|
|
14
|
+
linkUrl: 'link-url',
|
|
15
|
+
linkLabel: 'link-label'
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
_defineProperty(this, "ariaLabels", {
|
|
19
|
+
editLink: 'Edit link',
|
|
20
|
+
deleteLink: 'Remove',
|
|
21
|
+
unlinkLink: 'Unlink'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
this.cy = cy;
|
|
25
|
+
this.editor = editor;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getViewSwitcher() {
|
|
29
|
+
return this.cy.get(this.toTestId(this.testIds.viewSwitcher));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
openViewSwitcher() {
|
|
33
|
+
return this.getViewSwitcher().click();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getViewSwitcherOption(type = 'inline') {
|
|
37
|
+
switch (type) {
|
|
38
|
+
case 'url':
|
|
39
|
+
{
|
|
40
|
+
return this.cy.get(this.toTestId(this.testIds.displayUrlOption));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case 'inline':
|
|
44
|
+
{
|
|
45
|
+
return this.cy.get(this.toTestId(this.testIds.displayInlineOption));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
case 'block':
|
|
49
|
+
{
|
|
50
|
+
return this.cy.get(this.toTestId(this.testIds.displayCardOption));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
case 'embed':
|
|
54
|
+
{
|
|
55
|
+
return this.cy.get(this.toTestId(this.testIds.displayEmbedOption));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
default:
|
|
59
|
+
{
|
|
60
|
+
throw Error(`Attempted to switch to \`${type}\`: unknown Smart Link view switcher option!`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
selectViewSwitcherOption(type = 'inline') {
|
|
66
|
+
const viewSwitcherOption = this.getViewSwitcherOption(type);
|
|
67
|
+
return viewSwitcherOption.click();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
insertSmartLinkByTyping(url) {
|
|
71
|
+
return this.editor.getEditorArea().focus().type(`${url} {enter}`, {
|
|
72
|
+
delay: 0
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
switchAfterInsert(type = 'inline') {
|
|
77
|
+
// NOTE: inline inserted by default for all Smart Links.
|
|
78
|
+
const inlineCard = new InlineCardPageObject(this.cy);
|
|
79
|
+
inlineCard.click();
|
|
80
|
+
this.openViewSwitcher();
|
|
81
|
+
this.selectViewSwitcherOption(type);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getSmartLink(type = 'inline') {
|
|
85
|
+
switch (type) {
|
|
86
|
+
case 'inline':
|
|
87
|
+
{
|
|
88
|
+
return new InlineCardPageObject(this.cy);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case 'block':
|
|
92
|
+
{
|
|
93
|
+
return new BlockCardPageObject(this.cy);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
case 'embed':
|
|
97
|
+
{
|
|
98
|
+
return new EmbedCardPageObject(this.cy);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'url':
|
|
102
|
+
{
|
|
103
|
+
return new InlineCardPageObject(this.cy);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
getEditLinkButton() {
|
|
109
|
+
return this.cy.get(this.toAriaLabel(this.ariaLabels.editLink));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
openEditLinkMenu() {
|
|
113
|
+
const button = this.getEditLinkButton();
|
|
114
|
+
return button.click();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
changeLinkLabel(title, type = 'inline') {
|
|
118
|
+
const smartLink = this.getSmartLink(type);
|
|
119
|
+
smartLink.click();
|
|
120
|
+
this.openEditLinkMenu();
|
|
121
|
+
this.typeIntoLabelField(title);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
changeLinkUrl(url, type = 'inline') {
|
|
125
|
+
this.getSmartLink(type).click();
|
|
126
|
+
this.openEditLinkMenu();
|
|
127
|
+
this.typeIntoUrlField(url);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
typeIntoLabelField(title) {
|
|
131
|
+
const titleField = this.cy.get(this.toTestId(this.testIds.linkLabel)).focus();
|
|
132
|
+
titleField.clear();
|
|
133
|
+
titleField.type(`${title}{enter}`, {
|
|
134
|
+
delay: 0
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
typeIntoUrlField(url) {
|
|
139
|
+
const urlField = this.cy.get(this.toTestId(this.testIds.linkUrl)).focus();
|
|
140
|
+
urlField.clear();
|
|
141
|
+
urlField.type(`${url}{enter}`, {
|
|
142
|
+
delay: 0
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
deleteSmartLink(type = 'inline') {
|
|
147
|
+
this.getSmartLink(type).click();
|
|
148
|
+
this.clickDeleteIcon();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
clickDeleteIcon() {
|
|
152
|
+
const ariaLabelSelector = this.toAriaLabel(this.ariaLabels.deleteLink);
|
|
153
|
+
const deleteButtonSelector = `button${ariaLabelSelector}`;
|
|
154
|
+
return this.cy.get(deleteButtonSelector).click();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
unlink(type = 'inline') {
|
|
158
|
+
this.getSmartLink(type).click();
|
|
159
|
+
this.clickUnlinkIcon();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
clickUnlinkIcon() {
|
|
163
|
+
const ariaLabelSelector = this.toAriaLabel(this.ariaLabels.unlinkLink);
|
|
164
|
+
const unlinkButtonSelector = `button${ariaLabelSelector}`;
|
|
165
|
+
return this.cy.get(unlinkButtonSelector).click();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { InProductTestPageObject } from '@atlaskit/in-product-testing';
|
|
2
|
+
export class RendererPageObject extends InProductTestPageObject {
|
|
3
|
+
getContent() {
|
|
4
|
+
return this.cy.get('.ak-renderer-document');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
expectContentReady() {
|
|
8
|
+
return this.cy.get('.ak-renderer-document').should('be.visible');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
expectMediaSingleRenders(numOfMedia) {
|
|
12
|
+
return this.cy.get('.rich-media-item').should('have.length', numOfMedia).each(element => {
|
|
13
|
+
this.cy.wrap(element).should('have.attr', 'data-layout', 'center').should('have.attr', 'data-node-type', 'mediaSingle').should('be.visible');
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl-next';
|
|
2
|
+
export const codeBlockCopyButtonMessages = defineMessages({
|
|
3
|
+
copyCodeToClipboard: {
|
|
4
|
+
id: 'fabric.editor.codeBlockCopyButton.copyToClipboard',
|
|
5
|
+
defaultMessage: 'Copy as text',
|
|
6
|
+
description: 'Copy the content of the code block as text to your clipboard'
|
|
7
|
+
},
|
|
8
|
+
copiedCodeToClipboard: {
|
|
9
|
+
id: 'fabric.editor.codeBlockCopyButton.copiedToClipboard',
|
|
10
|
+
defaultMessage: 'Copied!',
|
|
11
|
+
description: 'Copied the content of the code block as text to clipboard'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { codeBidiWarningMessages } from './codeBidiWarning';
|
|
2
2
|
export { linkMessages } from './link';
|
|
3
|
-
export { unsupportedContentMessages } from './unsupportedContent';
|
|
3
|
+
export { unsupportedContentMessages } from './unsupportedContent';
|
|
4
|
+
export { codeBlockCopyButtonMessages } from './codeBlockCopyButton';
|
|
@@ -5,8 +5,10 @@ export const blockMarksSharedStyles = css`
|
|
|
5
5
|
* inside doc, tableCell, tableHeader, blockquote, etc.
|
|
6
6
|
*/
|
|
7
7
|
*:not(.fabric-editor-block-mark) >,
|
|
8
|
-
/* For nested block marks */
|
|
9
|
-
*:not(.fabric-editor-block-mark) > div.fabric-editor-block-mark:first-of-type
|
|
8
|
+
/* For nested block marks apart from those with indentation mark */
|
|
9
|
+
*:not(.fabric-editor-block-mark) > div.fabric-editor-block-mark:first-of-type:not(.fabric-editor-indentation-mark),
|
|
10
|
+
// If first document element has indentation mark remove margin-top
|
|
11
|
+
.ProseMirror .fabric-editor-indentation-mark:first-of-type:first-child {
|
|
10
12
|
p,
|
|
11
13
|
h1,
|
|
12
14
|
h2,
|