@electriccitizen/bolt 0.7.0 → 0.7.1
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/dist/plugins/linkit.d.ts +3 -0
- package/dist/plugins/linkit.js +144 -139
- package/dist/plugins/linkit.js.map +1 -1
- package/package.json +1 -1
package/dist/plugins/linkit.d.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Verifies: link dialog opens, Linkit autocomplete field is present,
|
|
5
5
|
* typing triggers suggestions, selecting a suggestion populates the URL.
|
|
6
|
+
*
|
|
7
|
+
* Tries multiple content types if the first one fails — different content types
|
|
8
|
+
* may use different text formats, and linkit may not be configured for all of them.
|
|
6
9
|
*/
|
|
7
10
|
import type { TestPlugin } from '../types.js';
|
|
8
11
|
export declare const linkitPlugin: TestPlugin;
|
package/dist/plugins/linkit.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Verifies: link dialog opens, Linkit autocomplete field is present,
|
|
5
5
|
* typing triggers suggestions, selecting a suggestion populates the URL.
|
|
6
|
+
*
|
|
7
|
+
* Tries multiple content types if the first one fails — different content types
|
|
8
|
+
* may use different text formats, and linkit may not be configured for all of them.
|
|
6
9
|
*/
|
|
7
10
|
export const linkitPlugin = {
|
|
8
11
|
name: 'linkit',
|
|
@@ -15,156 +18,158 @@ export const linkitPlugin = {
|
|
|
15
18
|
return profile.contentTypes.some((ct) => ct.fields.some((f) => f.type === 'text_long' || f.type === 'text_with_summary'));
|
|
16
19
|
},
|
|
17
20
|
async run(ctx) {
|
|
18
|
-
const results = [];
|
|
19
21
|
const { siteUrl, browserContext, profile } = ctx;
|
|
20
|
-
// Find
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
plugin: 'linkit',
|
|
25
|
-
test: 'linkit:no-type',
|
|
26
|
-
status: 'skip',
|
|
27
|
-
severity: 'info',
|
|
28
|
-
message: 'No content type with a CKEditor field found',
|
|
29
|
-
duration: 0,
|
|
30
|
-
});
|
|
31
|
-
return results;
|
|
32
|
-
}
|
|
33
|
-
const addUrl = `${siteUrl}/node/add/${testType.id}`;
|
|
34
|
-
let page;
|
|
35
|
-
try {
|
|
36
|
-
page = await browserContext.newPage();
|
|
37
|
-
await page.goto(addUrl, { waitUntil: 'networkidle', timeout: 30_000 });
|
|
38
|
-
// Wait for CKEditor to initialize.
|
|
39
|
-
const editable = page.locator('.ck-editor__editable[contenteditable="true"]').first();
|
|
40
|
-
if (await editable.count() === 0) {
|
|
41
|
-
results.push({
|
|
22
|
+
// Find content types with CKEditor fields.
|
|
23
|
+
const testTypes = profile.contentTypes.filter((ct) => ct.fields.some((f) => f.type === 'text_long' || f.type === 'text_with_summary'));
|
|
24
|
+
if (testTypes.length === 0) {
|
|
25
|
+
return [{
|
|
42
26
|
plugin: 'linkit',
|
|
43
|
-
test: 'linkit:no-
|
|
27
|
+
test: 'linkit:no-type',
|
|
44
28
|
status: 'skip',
|
|
45
29
|
severity: 'info',
|
|
46
|
-
message: '
|
|
30
|
+
message: 'No content type with a CKEditor field found',
|
|
47
31
|
duration: 0,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
await
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
const start = Date.now();
|
|
58
|
-
// Click the link button.
|
|
59
|
-
const linkButton = page.locator('.ck-toolbar button[data-cke-tooltip-text*="Link"], ' +
|
|
60
|
-
'.ck-toolbar button[data-cke-tooltip-text*="link"]').first();
|
|
61
|
-
if (await linkButton.count() === 0) {
|
|
62
|
-
results.push({
|
|
63
|
-
plugin: 'linkit',
|
|
64
|
-
test: 'linkit:link-button',
|
|
65
|
-
status: 'fail',
|
|
66
|
-
severity: 'major',
|
|
67
|
-
message: `${addUrl} — Link button not found in CKEditor toolbar`,
|
|
68
|
-
duration: Date.now() - start,
|
|
69
|
-
});
|
|
70
|
-
return results;
|
|
71
|
-
}
|
|
72
|
-
await linkButton.click();
|
|
73
|
-
await page.waitForTimeout(1500);
|
|
74
|
-
// Check for the link input (CKEditor's link balloon or Linkit's enhanced input).
|
|
75
|
-
const linkInput = page.locator('.ck-link-form .ck-input, ' +
|
|
76
|
-
'.ck-balloon-panel .ck-input, ' +
|
|
77
|
-
'.ck-labeled-field-view .ck-input').first();
|
|
78
|
-
const hasLinkInput = await linkInput.count() > 0;
|
|
79
|
-
const duration = Date.now() - start;
|
|
80
|
-
results.push({
|
|
81
|
-
plugin: 'linkit',
|
|
82
|
-
test: 'linkit:dialog-opens',
|
|
83
|
-
status: hasLinkInput ? 'pass' : 'fail',
|
|
84
|
-
severity: hasLinkInput ? 'info' : 'major',
|
|
85
|
-
message: hasLinkInput
|
|
86
|
-
? 'Link dialog opens with URL input field'
|
|
87
|
-
: `${addUrl} — Link dialog opened but no input field found`,
|
|
88
|
-
duration,
|
|
89
|
-
});
|
|
90
|
-
if (!hasLinkInput) {
|
|
91
|
-
await page.keyboard.press('Escape');
|
|
92
|
-
return results;
|
|
93
|
-
}
|
|
94
|
-
// Test 2: Linkit autocomplete — type a query and check for suggestions.
|
|
95
|
-
{
|
|
96
|
-
const start2 = Date.now();
|
|
97
|
-
await linkInput.fill('');
|
|
98
|
-
await linkInput.pressSequentially('about', { delay: 100 });
|
|
99
|
-
await page.waitForTimeout(2000);
|
|
100
|
-
// Linkit renders suggestions in a dropdown/autocomplete list.
|
|
101
|
-
const suggestions = page.locator('.ck-link-form .linkit-result, ' +
|
|
102
|
-
'.ck-balloon-panel .linkit-result, ' +
|
|
103
|
-
'.linkit-result-line, ' +
|
|
104
|
-
'.ck-reset_all .ck-list__item, ' +
|
|
105
|
-
'[class*="linkit"] li, ' +
|
|
106
|
-
'.ui-autocomplete li');
|
|
107
|
-
const suggestionCount = await suggestions.count();
|
|
108
|
-
const duration2 = Date.now() - start2;
|
|
109
|
-
if (suggestionCount > 0) {
|
|
110
|
-
// Test 3: Select a suggestion.
|
|
111
|
-
const selectStart = Date.now();
|
|
112
|
-
await suggestions.first().click();
|
|
113
|
-
await page.waitForTimeout(500);
|
|
114
|
-
// Check that the input now has a value.
|
|
115
|
-
const linkValue = await linkInput.inputValue().catch(() => '');
|
|
116
|
-
const hasValue = linkValue.length > 0;
|
|
117
|
-
const selectDuration = Date.now() - selectStart;
|
|
118
|
-
results.push({
|
|
119
|
-
plugin: 'linkit',
|
|
120
|
-
test: 'linkit:autocomplete',
|
|
121
|
-
status: 'pass',
|
|
122
|
-
severity: 'info',
|
|
123
|
-
message: `Linkit autocomplete returned ${suggestionCount} result(s) for "about"`,
|
|
124
|
-
duration: duration2,
|
|
125
|
-
});
|
|
126
|
-
results.push({
|
|
127
|
-
plugin: 'linkit',
|
|
128
|
-
test: 'linkit:selection',
|
|
129
|
-
status: hasValue ? 'pass' : 'warn',
|
|
130
|
-
severity: hasValue ? 'info' : 'minor',
|
|
131
|
-
message: hasValue
|
|
132
|
-
? `Linkit selection populated URL: ${linkValue.slice(0, 80)}`
|
|
133
|
-
: 'Linkit suggestion clicked but URL input not populated',
|
|
134
|
-
duration: selectDuration,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
results.push({
|
|
139
|
-
plugin: 'linkit',
|
|
140
|
-
test: 'linkit:autocomplete',
|
|
141
|
-
status: 'warn',
|
|
142
|
-
severity: 'minor',
|
|
143
|
-
message: 'Linkit autocomplete did not return suggestions for "about" — may not be configured for this text format',
|
|
144
|
-
duration: duration2,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
// Close the link dialog.
|
|
149
|
-
await page.keyboard.press('Escape');
|
|
150
|
-
await page.waitForTimeout(500);
|
|
32
|
+
}];
|
|
33
|
+
}
|
|
34
|
+
// Try content types until we find one where linkit autocomplete returns results.
|
|
35
|
+
// This handles the case where linkit is configured for some text formats but not others.
|
|
36
|
+
for (const testType of testTypes) {
|
|
37
|
+
const result = await testLinkitOnType(siteUrl, browserContext, testType.id, testType.label);
|
|
38
|
+
if (result.linkitWorked) {
|
|
39
|
+
return result.results;
|
|
151
40
|
}
|
|
41
|
+
// If linkit didn't work on this type, try the next one (don't accumulate results).
|
|
42
|
+
}
|
|
43
|
+
// None of the content types had working linkit autocomplete.
|
|
44
|
+
// Return the result from the last attempt with the type name.
|
|
45
|
+
const lastType = testTypes[testTypes.length - 1];
|
|
46
|
+
const lastResult = await testLinkitOnType(siteUrl, browserContext, lastType.id, lastType.label);
|
|
47
|
+
// Add note about which types were tried.
|
|
48
|
+
const triedTypes = testTypes.map((t) => t.id).join(', ');
|
|
49
|
+
lastResult.results.push({
|
|
50
|
+
plugin: 'linkit',
|
|
51
|
+
test: 'linkit:multi-type',
|
|
52
|
+
status: 'warn',
|
|
53
|
+
severity: 'minor',
|
|
54
|
+
message: `Tried ${testTypes.length} content types (${triedTypes}) — linkit autocomplete did not return results on any`,
|
|
55
|
+
duration: 0,
|
|
56
|
+
});
|
|
57
|
+
return lastResult.results;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Test linkit autocomplete on a specific content type.
|
|
62
|
+
* Returns results and whether linkit autocomplete actually returned suggestions.
|
|
63
|
+
*/
|
|
64
|
+
async function testLinkitOnType(siteUrl, browserContext, typeId, typeLabel) {
|
|
65
|
+
const results = [];
|
|
66
|
+
const addUrl = `${siteUrl}/node/add/${typeId}`;
|
|
67
|
+
let page;
|
|
68
|
+
let linkitWorked = false;
|
|
69
|
+
try {
|
|
70
|
+
page = await browserContext.newPage();
|
|
71
|
+
await page.goto(addUrl, { waitUntil: 'networkidle', timeout: 30_000 });
|
|
72
|
+
// Wait for CKEditor to initialize.
|
|
73
|
+
const editable = page.locator('.ck-editor__editable[contenteditable="true"]').first();
|
|
74
|
+
if (await editable.count() === 0) {
|
|
75
|
+
return { results: [], linkitWorked: false };
|
|
152
76
|
}
|
|
153
|
-
|
|
77
|
+
// Type some text to select for linking.
|
|
78
|
+
await editable.click();
|
|
79
|
+
await editable.pressSequentially('Link test text', { delay: 20 });
|
|
80
|
+
await page.keyboard.press('Control+a');
|
|
81
|
+
// Test 1: Open link dialog.
|
|
82
|
+
const linkStart = Date.now();
|
|
83
|
+
const linkButton = page.locator('.ck-toolbar button[data-cke-tooltip-text*="Link"], ' +
|
|
84
|
+
'.ck-toolbar button[data-cke-tooltip-text*="link"]').first();
|
|
85
|
+
if (await linkButton.count() === 0) {
|
|
154
86
|
results.push({
|
|
155
87
|
plugin: 'linkit',
|
|
156
|
-
test: 'linkit:
|
|
88
|
+
test: 'linkit:link-button',
|
|
157
89
|
status: 'fail',
|
|
158
|
-
severity: '
|
|
159
|
-
message:
|
|
160
|
-
duration:
|
|
90
|
+
severity: 'major',
|
|
91
|
+
message: `${addUrl} — Link button not found in CKEditor toolbar`,
|
|
92
|
+
duration: Date.now() - linkStart,
|
|
161
93
|
});
|
|
94
|
+
return { results, linkitWorked: false };
|
|
162
95
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
96
|
+
await linkButton.click();
|
|
97
|
+
await page.waitForTimeout(1500);
|
|
98
|
+
const linkInput = page.locator('.ck-link-form .ck-input, ' +
|
|
99
|
+
'.ck-balloon-panel .ck-input, ' +
|
|
100
|
+
'.ck-labeled-field-view .ck-input').first();
|
|
101
|
+
const hasLinkInput = await linkInput.count() > 0;
|
|
102
|
+
results.push({
|
|
103
|
+
plugin: 'linkit',
|
|
104
|
+
test: 'linkit:dialog-opens',
|
|
105
|
+
status: hasLinkInput ? 'pass' : 'fail',
|
|
106
|
+
severity: hasLinkInput ? 'info' : 'major',
|
|
107
|
+
message: hasLinkInput
|
|
108
|
+
? `Link dialog opens with URL input field (${typeLabel})`
|
|
109
|
+
: `${addUrl} — Link dialog opened but no input field found`,
|
|
110
|
+
duration: Date.now() - linkStart,
|
|
111
|
+
});
|
|
112
|
+
if (!hasLinkInput) {
|
|
113
|
+
await page.keyboard.press('Escape');
|
|
114
|
+
return { results, linkitWorked: false };
|
|
166
115
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
116
|
+
// Test 2: Linkit autocomplete.
|
|
117
|
+
const acStart = Date.now();
|
|
118
|
+
await linkInput.fill('');
|
|
119
|
+
await linkInput.pressSequentially('about', { delay: 100 });
|
|
120
|
+
await page.waitForTimeout(2000);
|
|
121
|
+
const suggestions = page.locator('.ck-link-form .linkit-result, ' +
|
|
122
|
+
'.ck-balloon-panel .linkit-result, ' +
|
|
123
|
+
'.linkit-result-line, ' +
|
|
124
|
+
'.ck-reset_all .ck-list__item, ' +
|
|
125
|
+
'[class*="linkit"] li, ' +
|
|
126
|
+
'.ui-autocomplete li');
|
|
127
|
+
const suggestionCount = await suggestions.count();
|
|
128
|
+
if (suggestionCount > 0) {
|
|
129
|
+
linkitWorked = true;
|
|
130
|
+
// Test 3: Select a suggestion.
|
|
131
|
+
const selectStart = Date.now();
|
|
132
|
+
await suggestions.first().click();
|
|
133
|
+
await page.waitForTimeout(500);
|
|
134
|
+
const linkValue = await linkInput.inputValue().catch(() => '');
|
|
135
|
+
const hasValue = linkValue.length > 0;
|
|
136
|
+
results.push({
|
|
137
|
+
plugin: 'linkit',
|
|
138
|
+
test: 'linkit:autocomplete',
|
|
139
|
+
status: 'pass',
|
|
140
|
+
severity: 'info',
|
|
141
|
+
message: `Linkit autocomplete returned ${suggestionCount} result(s) for "about" on ${typeLabel}`,
|
|
142
|
+
duration: Date.now() - acStart,
|
|
143
|
+
});
|
|
144
|
+
results.push({
|
|
145
|
+
plugin: 'linkit',
|
|
146
|
+
test: 'linkit:selection',
|
|
147
|
+
status: hasValue ? 'pass' : 'warn',
|
|
148
|
+
severity: hasValue ? 'info' : 'minor',
|
|
149
|
+
message: hasValue
|
|
150
|
+
? `Linkit selection populated URL: ${linkValue.slice(0, 80)}`
|
|
151
|
+
: 'Linkit suggestion clicked but URL input not populated',
|
|
152
|
+
duration: Date.now() - selectStart,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// If no suggestions, don't add a result — we'll try the next content type.
|
|
156
|
+
await page.keyboard.press('Escape');
|
|
157
|
+
await page.waitForTimeout(500);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
results.push({
|
|
161
|
+
plugin: 'linkit',
|
|
162
|
+
test: 'linkit:crash',
|
|
163
|
+
status: 'fail',
|
|
164
|
+
severity: 'critical',
|
|
165
|
+
message: `Linkit test failed on ${typeLabel}: ${err instanceof Error ? err.message : String(err)}`,
|
|
166
|
+
duration: 0,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
if (page)
|
|
171
|
+
await page.close();
|
|
172
|
+
}
|
|
173
|
+
return { results, linkitWorked };
|
|
174
|
+
}
|
|
170
175
|
//# sourceMappingURL=linkit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linkit.js","sourceRoot":"","sources":["../../src/plugins/linkit.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"linkit.js","sourceRoot":"","sources":["../../src/plugins/linkit.ts"],"names":[],"mappings":"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"}
|