pict-section-inlinedocumentation 0.0.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/README.md +107 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +83 -0
- package/docs/_cover.md +15 -0
- package/docs/_sidebar.md +24 -0
- package/docs/_topbar.md +8 -0
- package/docs/_version.json +7 -0
- package/docs/api-reference.md +185 -0
- package/docs/architecture.md +103 -0
- package/docs/css/docuserve.css +327 -0
- package/docs/embedding-level1-sidebar.md +92 -0
- package/docs/embedding-level2-routes.md +86 -0
- package/docs/embedding-level3-tooltips.md +97 -0
- package/docs/embedding-level4-autogen.md +126 -0
- package/docs/index.html +39 -0
- package/docs/overview.md +42 -0
- package/docs/quickstart.md +95 -0
- package/docs/reference.md +73 -0
- package/docs/retold-catalog.json +181 -0
- package/docs/retold-keyword-index.json +4374 -0
- package/example_applications/basic/docs/README.md +40 -0
- package/example_applications/basic/docs/_sidebar.md +4 -0
- package/example_applications/basic/docs/_topics.json +10 -0
- package/example_applications/basic/docs/advanced-topics.md +47 -0
- package/example_applications/basic/docs/getting-started.md +70 -0
- package/example_applications/basic/index.html +100 -0
- package/example_applications/bookshop/.quackage.json +10 -0
- package/example_applications/bookshop/Pict-Application-Bookshop-Configuration.json +15 -0
- package/example_applications/bookshop/Pict-Application-Bookshop.js +218 -0
- package/example_applications/bookshop/data/BookshopData.json +65 -0
- package/example_applications/bookshop/data/pict_documentation_topics.json +46 -0
- package/example_applications/bookshop/docs/_sidebar.md +6 -0
- package/example_applications/bookshop/docs/book-detail.md +21 -0
- package/example_applications/bookshop/docs/book-list.md +21 -0
- package/example_applications/bookshop/docs/search-filter.md +18 -0
- package/example_applications/bookshop/docs/store.md +29 -0
- package/example_applications/bookshop/docs/welcome.md +23 -0
- package/example_applications/bookshop/html/index.html +236 -0
- package/example_applications/bookshop/package.json +34 -0
- package/example_applications/bookshop/views/PictView-Bookshop-BookList.js +324 -0
- package/example_applications/bookshop/views/PictView-Bookshop-HelpToggle.js +44 -0
- package/example_applications/bookshop/views/PictView-Bookshop-Store.js +271 -0
- package/package.json +55 -0
- package/source/Pict-Section-InlineDocumentation.js +10 -0
- package/source/providers/Pict-Provider-InlineDocumentation.js +1995 -0
- package/source/views/Pict-View-InlineDocumentation-Content.js +542 -0
- package/source/views/Pict-View-InlineDocumentation-Layout.js +206 -0
- package/source/views/Pict-View-InlineDocumentation-Nav.js +475 -0
- package/source/views/Pict-View-InlineDocumentation-TopicManager.js +1623 -0
- package/test/Browser_Integration_tests.js +1449 -0
- package/test/Pict-Section-InlineDocumentation_test.js +1285 -0
|
@@ -0,0 +1,1449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless browser integration tests for pict-section-inlinedocumentation.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the inline documentation bookshop example works in a real browser:
|
|
5
|
+
* 1) The bookshop loads and renders the book catalog
|
|
6
|
+
* 2) The help panel opens via button and F1 keyboard shortcut
|
|
7
|
+
* 3) Browsing different screens loads the correct contextual help
|
|
8
|
+
* 4) Editing existing documentation (edit, modify, save, cancel)
|
|
9
|
+
* 5) Creating a new documentation topic at runtime
|
|
10
|
+
* 6) Binding a documentation entry to a route
|
|
11
|
+
* 7) Multiple topics matching a route — navigation between them
|
|
12
|
+
*
|
|
13
|
+
* Screenshots and logs are saved to dist/test-artifacts/ after each key stage.
|
|
14
|
+
*
|
|
15
|
+
* Serves the pre-built bookshop example dist/ folder from a local HTTP server.
|
|
16
|
+
*
|
|
17
|
+
* Requires:
|
|
18
|
+
* - cd example_applications/bookshop && npx quack build && npx quack copy
|
|
19
|
+
* - npm install (puppeteer must be available)
|
|
20
|
+
*
|
|
21
|
+
* @license MIT
|
|
22
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const Chai = require('chai');
|
|
26
|
+
const Expect = Chai.expect;
|
|
27
|
+
|
|
28
|
+
const libHTTP = require('http');
|
|
29
|
+
const libFS = require('fs');
|
|
30
|
+
const libPath = require('path');
|
|
31
|
+
|
|
32
|
+
const _PackageRoot = libPath.resolve(__dirname, '..');
|
|
33
|
+
const _BookshopDistDir = libPath.join(_PackageRoot, 'example_applications', 'bookshop', 'dist');
|
|
34
|
+
const _ArtifactsDir = libPath.join(_PackageRoot, 'dist', 'test-artifacts');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Ensure the test artifacts directory exists.
|
|
38
|
+
*/
|
|
39
|
+
function ensureArtifactsDir()
|
|
40
|
+
{
|
|
41
|
+
if (!libFS.existsSync(libPath.join(_PackageRoot, 'dist')))
|
|
42
|
+
{
|
|
43
|
+
libFS.mkdirSync(libPath.join(_PackageRoot, 'dist'));
|
|
44
|
+
}
|
|
45
|
+
if (!libFS.existsSync(_ArtifactsDir))
|
|
46
|
+
{
|
|
47
|
+
libFS.mkdirSync(_ArtifactsDir);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a simple HTTP server that serves the bookshop dist/ folder.
|
|
53
|
+
*
|
|
54
|
+
* @param {function} fCallback - Callback with (pError, pServer, pPort)
|
|
55
|
+
*/
|
|
56
|
+
function startTestServer(fCallback)
|
|
57
|
+
{
|
|
58
|
+
let tmpMimeTypes =
|
|
59
|
+
{
|
|
60
|
+
'.html': 'text/html',
|
|
61
|
+
'.js': 'application/javascript',
|
|
62
|
+
'.json': 'application/json',
|
|
63
|
+
'.md': 'text/markdown',
|
|
64
|
+
'.map': 'application/json',
|
|
65
|
+
'.css': 'text/css'
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
let tmpServer = libHTTP.createServer(
|
|
69
|
+
(pRequest, pResponse) =>
|
|
70
|
+
{
|
|
71
|
+
let tmpUrl = pRequest.url;
|
|
72
|
+
|
|
73
|
+
// Strip query strings
|
|
74
|
+
let tmpQueryIndex = tmpUrl.indexOf('?');
|
|
75
|
+
if (tmpQueryIndex >= 0)
|
|
76
|
+
{
|
|
77
|
+
tmpUrl = tmpUrl.substring(0, tmpQueryIndex);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Default to index.html
|
|
81
|
+
if (tmpUrl === '/')
|
|
82
|
+
{
|
|
83
|
+
tmpUrl = '/index.html';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Serve from dist/
|
|
87
|
+
let tmpFilePath = libPath.join(_BookshopDistDir, tmpUrl);
|
|
88
|
+
|
|
89
|
+
if (!libFS.existsSync(tmpFilePath))
|
|
90
|
+
{
|
|
91
|
+
pResponse.writeHead(404);
|
|
92
|
+
pResponse.end('Not Found: ' + tmpUrl);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let tmpStat = libFS.statSync(tmpFilePath);
|
|
97
|
+
if (tmpStat.isDirectory())
|
|
98
|
+
{
|
|
99
|
+
tmpFilePath = libPath.join(tmpFilePath, 'index.html');
|
|
100
|
+
if (!libFS.existsSync(tmpFilePath))
|
|
101
|
+
{
|
|
102
|
+
pResponse.writeHead(404);
|
|
103
|
+
pResponse.end('Not Found: ' + tmpUrl);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let tmpExt = libPath.extname(tmpFilePath);
|
|
109
|
+
let tmpContentType = tmpMimeTypes[tmpExt] || 'application/octet-stream';
|
|
110
|
+
|
|
111
|
+
let tmpContent = libFS.readFileSync(tmpFilePath);
|
|
112
|
+
pResponse.writeHead(200, { 'Content-Type': tmpContentType });
|
|
113
|
+
pResponse.end(tmpContent);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
tmpServer.listen(0, '127.0.0.1',
|
|
117
|
+
() =>
|
|
118
|
+
{
|
|
119
|
+
let tmpPort = tmpServer.address().port;
|
|
120
|
+
return fCallback(null, tmpServer, tmpPort);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
suite
|
|
126
|
+
(
|
|
127
|
+
'Browser-Integration',
|
|
128
|
+
function()
|
|
129
|
+
{
|
|
130
|
+
// Browser tests need extra time for puppeteer startup and page operations
|
|
131
|
+
this.timeout(60000);
|
|
132
|
+
|
|
133
|
+
let _Server;
|
|
134
|
+
let _Port;
|
|
135
|
+
let _Browser;
|
|
136
|
+
let _Page;
|
|
137
|
+
let _Puppeteer;
|
|
138
|
+
|
|
139
|
+
// Collected console log entries across the entire run
|
|
140
|
+
let _ConsoleLog = [];
|
|
141
|
+
// Track screenshots taken
|
|
142
|
+
let _ScreenshotsTaken = [];
|
|
143
|
+
// Test results for the summary log
|
|
144
|
+
let _TestResults = [];
|
|
145
|
+
// Run start time
|
|
146
|
+
let _RunStartTime;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Take a screenshot and save it to the artifacts dir.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} pName - Descriptive name (used in filename)
|
|
152
|
+
* @returns {Promise}
|
|
153
|
+
*/
|
|
154
|
+
function captureScreenshot(pName)
|
|
155
|
+
{
|
|
156
|
+
let tmpFilename = pName.replace(/[^a-zA-Z0-9_-]/g, '_') + '.png';
|
|
157
|
+
let tmpPath = libPath.join(_ArtifactsDir, tmpFilename);
|
|
158
|
+
|
|
159
|
+
return _Page.screenshot({ path: tmpPath, fullPage: true })
|
|
160
|
+
.then(function()
|
|
161
|
+
{
|
|
162
|
+
_ScreenshotsTaken.push({ name: pName, file: tmpFilename, timestamp: new Date().toISOString() });
|
|
163
|
+
})
|
|
164
|
+
.catch(function(pError)
|
|
165
|
+
{
|
|
166
|
+
// Screenshot failures should not break the test — log and move on
|
|
167
|
+
_ScreenshotsTaken.push({ name: pName, file: tmpFilename, timestamp: new Date().toISOString(), error: pError.message });
|
|
168
|
+
console.log(' [screenshot warning] ' + pName + ': ' + pError.message);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Capture a state log entry — collects page state and appdata at this moment.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} pStageName - Descriptive stage name
|
|
176
|
+
* @returns {Promise<Object>} The captured state
|
|
177
|
+
*/
|
|
178
|
+
function captureStateLog(pStageName)
|
|
179
|
+
{
|
|
180
|
+
return _Page.evaluate(function()
|
|
181
|
+
{
|
|
182
|
+
var state = window._Pict && window._Pict.AppData && window._Pict.AppData.InlineDocumentation
|
|
183
|
+
? window._Pict.AppData.InlineDocumentation : {};
|
|
184
|
+
var appState = window._Pict && window._Pict.AppData && window._Pict.AppData.Bookshop
|
|
185
|
+
? window._Pict.AppData.Bookshop : {};
|
|
186
|
+
var provider = window._Pict && window._Pict.providers
|
|
187
|
+
? window._Pict.providers['Pict-InlineDocumentation'] : null;
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
CurrentPath: state.CurrentPath || '',
|
|
191
|
+
CurrentRoute: state.CurrentRoute || '',
|
|
192
|
+
Topic: state.Topic || null,
|
|
193
|
+
EditEnabled: !!state.EditEnabled,
|
|
194
|
+
Editing: !!state.Editing,
|
|
195
|
+
EditingPath: state.EditingPath || '',
|
|
196
|
+
CurrentView: appState.CurrentView || '',
|
|
197
|
+
CurrentBook: appState.CurrentBook ? appState.CurrentBook.Title : null,
|
|
198
|
+
HelpVisible: !!appState.HelpVisible,
|
|
199
|
+
TopicCount: state.Topics ? Object.keys(state.Topics).length : 0,
|
|
200
|
+
CacheCount: provider ? Object.keys(provider._ContentCache).length : 0,
|
|
201
|
+
PanelVisible: document.getElementById('Bookshop-Help-Panel')
|
|
202
|
+
? document.getElementById('Bookshop-Help-Panel').classList.contains('visible')
|
|
203
|
+
: false
|
|
204
|
+
};
|
|
205
|
+
})
|
|
206
|
+
.then(function(pState)
|
|
207
|
+
{
|
|
208
|
+
pState._stage = pStageName;
|
|
209
|
+
pState._timestamp = new Date().toISOString();
|
|
210
|
+
_ConsoleLog.push(pState);
|
|
211
|
+
return pState;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Write all collected logs and the summary to disk.
|
|
217
|
+
*/
|
|
218
|
+
function writeSummaryLog()
|
|
219
|
+
{
|
|
220
|
+
let tmpSummary =
|
|
221
|
+
{
|
|
222
|
+
runDate: _RunStartTime,
|
|
223
|
+
runDurationMs: Date.now() - new Date(_RunStartTime).getTime(),
|
|
224
|
+
testsRun: _TestResults.length,
|
|
225
|
+
testsPassed: _TestResults.filter(function(r) { return r.passed; }).length,
|
|
226
|
+
testsFailed: _TestResults.filter(function(r) { return !r.passed; }).length,
|
|
227
|
+
tests: _TestResults,
|
|
228
|
+
screenshots: _ScreenshotsTaken,
|
|
229
|
+
stateLog: _ConsoleLog
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
let tmpLogPath = libPath.join(_ArtifactsDir, 'test-run-summary.json');
|
|
233
|
+
libFS.writeFileSync(tmpLogPath, JSON.stringify(tmpSummary, null, '\t'));
|
|
234
|
+
|
|
235
|
+
// Also write a human-readable log
|
|
236
|
+
let tmpLines = [];
|
|
237
|
+
tmpLines.push('=== Browser Integration Test Run ===');
|
|
238
|
+
tmpLines.push('Date: ' + tmpSummary.runDate);
|
|
239
|
+
tmpLines.push('Duration: ' + tmpSummary.runDurationMs + 'ms');
|
|
240
|
+
tmpLines.push('Tests: ' + tmpSummary.testsPassed + '/' + tmpSummary.testsRun + ' passed');
|
|
241
|
+
tmpLines.push('');
|
|
242
|
+
tmpLines.push('--- Test Results ---');
|
|
243
|
+
for (let i = 0; i < _TestResults.length; i++)
|
|
244
|
+
{
|
|
245
|
+
let tmpR = _TestResults[i];
|
|
246
|
+
tmpLines.push((tmpR.passed ? 'PASS' : 'FAIL') + ': ' + tmpR.name + (tmpR.error ? ' (' + tmpR.error + ')' : ''));
|
|
247
|
+
}
|
|
248
|
+
tmpLines.push('');
|
|
249
|
+
tmpLines.push('--- Screenshots ---');
|
|
250
|
+
for (let i = 0; i < _ScreenshotsTaken.length; i++)
|
|
251
|
+
{
|
|
252
|
+
let tmpS = _ScreenshotsTaken[i];
|
|
253
|
+
tmpLines.push('[' + tmpS.timestamp + '] ' + tmpS.name + ' -> ' + tmpS.file);
|
|
254
|
+
}
|
|
255
|
+
tmpLines.push('');
|
|
256
|
+
tmpLines.push('--- State Log ---');
|
|
257
|
+
for (let i = 0; i < _ConsoleLog.length; i++)
|
|
258
|
+
{
|
|
259
|
+
let tmpEntry = _ConsoleLog[i];
|
|
260
|
+
tmpLines.push('[' + tmpEntry._timestamp + '] ' + tmpEntry._stage);
|
|
261
|
+
tmpLines.push(' View=' + tmpEntry.CurrentView + ' Path=' + tmpEntry.CurrentPath + ' Route=' + tmpEntry.CurrentRoute);
|
|
262
|
+
tmpLines.push(' Topic=' + tmpEntry.Topic + ' Editing=' + tmpEntry.Editing + ' PanelVisible=' + tmpEntry.PanelVisible);
|
|
263
|
+
}
|
|
264
|
+
tmpLines.push('');
|
|
265
|
+
|
|
266
|
+
let tmpTextPath = libPath.join(_ArtifactsDir, 'test-run-summary.log');
|
|
267
|
+
libFS.writeFileSync(tmpTextPath, tmpLines.join('\n'));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Record a test result.
|
|
272
|
+
*
|
|
273
|
+
* @param {string} pName - Test name
|
|
274
|
+
* @param {boolean} pPassed - Whether it passed
|
|
275
|
+
* @param {string} [pError] - Error message if failed
|
|
276
|
+
*/
|
|
277
|
+
function recordTestResult(pName, pPassed, pError)
|
|
278
|
+
{
|
|
279
|
+
_TestResults.push({ name: pName, passed: pPassed, error: pError || null, timestamp: new Date().toISOString() });
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
suiteSetup
|
|
284
|
+
(
|
|
285
|
+
function(fDone)
|
|
286
|
+
{
|
|
287
|
+
_RunStartTime = new Date().toISOString();
|
|
288
|
+
|
|
289
|
+
// Ensure artifacts dir
|
|
290
|
+
ensureArtifactsDir();
|
|
291
|
+
|
|
292
|
+
// Verify dist/ exists
|
|
293
|
+
if (!libFS.existsSync(libPath.join(_BookshopDistDir, 'index.html')))
|
|
294
|
+
{
|
|
295
|
+
return fDone(new Error(
|
|
296
|
+
'Bookshop dist/index.html not found. Run "cd example_applications/bookshop && npx quack build && npx quack copy" first.'
|
|
297
|
+
));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!libFS.existsSync(libPath.join(_BookshopDistDir, 'bookshop_example.js')))
|
|
301
|
+
{
|
|
302
|
+
return fDone(new Error(
|
|
303
|
+
'Bookshop dist/bookshop_example.js not found. Run "cd example_applications/bookshop && npx quack build" first.'
|
|
304
|
+
));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Start the test server
|
|
308
|
+
startTestServer(
|
|
309
|
+
function(pError, pServer, pPort)
|
|
310
|
+
{
|
|
311
|
+
if (pError)
|
|
312
|
+
{
|
|
313
|
+
return fDone(pError);
|
|
314
|
+
}
|
|
315
|
+
_Server = pServer;
|
|
316
|
+
_Port = pPort;
|
|
317
|
+
|
|
318
|
+
// Load puppeteer
|
|
319
|
+
try
|
|
320
|
+
{
|
|
321
|
+
_Puppeteer = require('puppeteer');
|
|
322
|
+
}
|
|
323
|
+
catch (pRequireError)
|
|
324
|
+
{
|
|
325
|
+
_Server.close();
|
|
326
|
+
return fDone(new Error(
|
|
327
|
+
'puppeteer is not installed. Run "npm install" to install it as a devDependency.'
|
|
328
|
+
));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Launch browser
|
|
332
|
+
_Puppeteer.launch(
|
|
333
|
+
{
|
|
334
|
+
headless: true,
|
|
335
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
336
|
+
protocolTimeout: 60000
|
|
337
|
+
})
|
|
338
|
+
.then(
|
|
339
|
+
function(pBrowser)
|
|
340
|
+
{
|
|
341
|
+
_Browser = pBrowser;
|
|
342
|
+
return _Browser.newPage();
|
|
343
|
+
})
|
|
344
|
+
.then(
|
|
345
|
+
function(pPage)
|
|
346
|
+
{
|
|
347
|
+
_Page = pPage;
|
|
348
|
+
|
|
349
|
+
// Set a reasonable viewport
|
|
350
|
+
return _Page.setViewport({ width: 1400, height: 900 });
|
|
351
|
+
})
|
|
352
|
+
.then(
|
|
353
|
+
function()
|
|
354
|
+
{
|
|
355
|
+
// Capture all console output
|
|
356
|
+
_Page.on('console',
|
|
357
|
+
function(pMessage)
|
|
358
|
+
{
|
|
359
|
+
if (pMessage.type() === 'error')
|
|
360
|
+
{
|
|
361
|
+
console.log(' [browser error]', pMessage.text());
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
_Page.on('pageerror',
|
|
366
|
+
function(pError)
|
|
367
|
+
{
|
|
368
|
+
console.log(' [browser page error]', pError.message);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
return fDone();
|
|
372
|
+
})
|
|
373
|
+
.catch(
|
|
374
|
+
function(pError)
|
|
375
|
+
{
|
|
376
|
+
_Server.close();
|
|
377
|
+
return fDone(pError);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
suiteTeardown
|
|
384
|
+
(
|
|
385
|
+
function(fDone)
|
|
386
|
+
{
|
|
387
|
+
// Write all logs and summary
|
|
388
|
+
try
|
|
389
|
+
{
|
|
390
|
+
writeSummaryLog();
|
|
391
|
+
console.log(' Artifacts saved to: dist/test-artifacts/');
|
|
392
|
+
console.log(' Screenshots: ' + _ScreenshotsTaken.length);
|
|
393
|
+
}
|
|
394
|
+
catch (pWriteError)
|
|
395
|
+
{
|
|
396
|
+
console.log(' Warning: could not write test artifacts:', pWriteError.message);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
let tmpCleanupSteps = [];
|
|
400
|
+
|
|
401
|
+
if (_Browser)
|
|
402
|
+
{
|
|
403
|
+
tmpCleanupSteps.push(_Browser.close().catch(function() {}));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
Promise.all(tmpCleanupSteps).then(
|
|
407
|
+
function()
|
|
408
|
+
{
|
|
409
|
+
if (_Server)
|
|
410
|
+
{
|
|
411
|
+
_Server.close(fDone);
|
|
412
|
+
}
|
|
413
|
+
else
|
|
414
|
+
{
|
|
415
|
+
fDone();
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
// ====================================================================
|
|
423
|
+
// Test 1: Application loads and renders the book catalog
|
|
424
|
+
// ====================================================================
|
|
425
|
+
test
|
|
426
|
+
(
|
|
427
|
+
'Bookshop loads and renders the book catalog',
|
|
428
|
+
function(fDone)
|
|
429
|
+
{
|
|
430
|
+
_Page.goto(`http://127.0.0.1:${_Port}/`, { waitUntil: 'networkidle0', timeout: 15000 })
|
|
431
|
+
.then(function()
|
|
432
|
+
{
|
|
433
|
+
return _Page.waitForSelector('.bookshop-book-card', { timeout: 10000 });
|
|
434
|
+
})
|
|
435
|
+
.then(function()
|
|
436
|
+
{
|
|
437
|
+
return captureScreenshot('01-book-catalog-loaded');
|
|
438
|
+
})
|
|
439
|
+
.then(function()
|
|
440
|
+
{
|
|
441
|
+
return captureStateLog('01-book-catalog-loaded');
|
|
442
|
+
})
|
|
443
|
+
.then(function()
|
|
444
|
+
{
|
|
445
|
+
return _Page.evaluate(function()
|
|
446
|
+
{
|
|
447
|
+
var cards = document.querySelectorAll('.bookshop-book-card');
|
|
448
|
+
var title = document.querySelector('.bookshop-section-title');
|
|
449
|
+
return {
|
|
450
|
+
cardCount: cards.length,
|
|
451
|
+
titleText: title ? title.textContent : null,
|
|
452
|
+
hasPict: typeof window._Pict !== 'undefined',
|
|
453
|
+
hasProvider: !!(window._Pict && window._Pict.providers && window._Pict.providers['Pict-InlineDocumentation'])
|
|
454
|
+
};
|
|
455
|
+
});
|
|
456
|
+
})
|
|
457
|
+
.then(function(pResult)
|
|
458
|
+
{
|
|
459
|
+
Expect(pResult.cardCount).to.be.above(0, 'Should have at least one book card');
|
|
460
|
+
Expect(pResult.titleText).to.equal('Book Catalog');
|
|
461
|
+
Expect(pResult.hasPict).to.be.true;
|
|
462
|
+
Expect(pResult.hasProvider).to.be.true;
|
|
463
|
+
recordTestResult('Bookshop loads and renders the book catalog', true);
|
|
464
|
+
fDone();
|
|
465
|
+
})
|
|
466
|
+
.catch(function(pError) { recordTestResult('Bookshop loads and renders the book catalog', false, pError.message); fDone(pError); });
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
// ====================================================================
|
|
472
|
+
// Test 2: Help panel opens via header button and shows content
|
|
473
|
+
// ====================================================================
|
|
474
|
+
test
|
|
475
|
+
(
|
|
476
|
+
'Help panel opens via header button and shows route-matched content',
|
|
477
|
+
function(fDone)
|
|
478
|
+
{
|
|
479
|
+
_Page.evaluate(function()
|
|
480
|
+
{
|
|
481
|
+
var panel = document.getElementById('Bookshop-Help-Panel');
|
|
482
|
+
return panel ? panel.classList.contains('visible') : false;
|
|
483
|
+
})
|
|
484
|
+
.then(function(pWasVisible)
|
|
485
|
+
{
|
|
486
|
+
if (pWasVisible)
|
|
487
|
+
{
|
|
488
|
+
return _Page.evaluate(function() { document.getElementById('Bookshop-Help-CloseBtn').click(); });
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
.then(function()
|
|
492
|
+
{
|
|
493
|
+
return _Page.click('#Bookshop-Header-HelpBtn');
|
|
494
|
+
})
|
|
495
|
+
.then(function()
|
|
496
|
+
{
|
|
497
|
+
return _Page.waitForFunction(
|
|
498
|
+
'document.getElementById("Bookshop-Help-Panel") && document.getElementById("Bookshop-Help-Panel").classList.contains("visible")',
|
|
499
|
+
{ timeout: 5000 }
|
|
500
|
+
);
|
|
501
|
+
})
|
|
502
|
+
.then(function()
|
|
503
|
+
{
|
|
504
|
+
return captureScreenshot('02-help-panel-open-book-list');
|
|
505
|
+
})
|
|
506
|
+
.then(function()
|
|
507
|
+
{
|
|
508
|
+
return captureStateLog('02-help-panel-open-book-list');
|
|
509
|
+
})
|
|
510
|
+
.then(function()
|
|
511
|
+
{
|
|
512
|
+
return _Page.evaluate(function()
|
|
513
|
+
{
|
|
514
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
515
|
+
var body = document.getElementById('InlineDoc-Content-Body');
|
|
516
|
+
return {
|
|
517
|
+
panelVisible: document.getElementById('Bookshop-Help-Panel').classList.contains('visible'),
|
|
518
|
+
currentPath: state.CurrentPath,
|
|
519
|
+
currentRoute: state.CurrentRoute,
|
|
520
|
+
topic: state.Topic,
|
|
521
|
+
bodyHasContent: body ? body.innerHTML.length > 50 : false
|
|
522
|
+
};
|
|
523
|
+
});
|
|
524
|
+
})
|
|
525
|
+
.then(function(pResult)
|
|
526
|
+
{
|
|
527
|
+
Expect(pResult.panelVisible).to.be.true;
|
|
528
|
+
Expect(pResult.currentPath).to.equal('book-list.md', 'Should load book-list.md for /books route');
|
|
529
|
+
Expect(pResult.topic).to.equal('BOOKSHOP-BOOKLIST');
|
|
530
|
+
Expect(pResult.bodyHasContent).to.be.true;
|
|
531
|
+
recordTestResult('Help panel opens via header button and shows route-matched content', true);
|
|
532
|
+
fDone();
|
|
533
|
+
})
|
|
534
|
+
.catch(function(pError) { recordTestResult('Help panel opens via header button and shows route-matched content', false, pError.message); fDone(pError); });
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
// ====================================================================
|
|
540
|
+
// Test 3: Navigate to store page and help updates via route
|
|
541
|
+
// ====================================================================
|
|
542
|
+
test
|
|
543
|
+
(
|
|
544
|
+
'Navigate to store page and help updates to store topic via route matching',
|
|
545
|
+
function(fDone)
|
|
546
|
+
{
|
|
547
|
+
_Page.evaluate(function()
|
|
548
|
+
{
|
|
549
|
+
var panel = document.getElementById('Bookshop-Help-Panel');
|
|
550
|
+
if (panel && panel.classList.contains('visible'))
|
|
551
|
+
{
|
|
552
|
+
window._Pict.PictApplication.toggleHelp();
|
|
553
|
+
}
|
|
554
|
+
})
|
|
555
|
+
.then(function()
|
|
556
|
+
{
|
|
557
|
+
return _Page.evaluate(function() { document.querySelector('.bookshop-book-card').click(); });
|
|
558
|
+
})
|
|
559
|
+
.then(function()
|
|
560
|
+
{
|
|
561
|
+
return _Page.waitForSelector('.bookshop-store-detail', { timeout: 5000 });
|
|
562
|
+
})
|
|
563
|
+
.then(function()
|
|
564
|
+
{
|
|
565
|
+
return captureScreenshot('03-store-page-navigated');
|
|
566
|
+
})
|
|
567
|
+
.then(function()
|
|
568
|
+
{
|
|
569
|
+
return captureStateLog('03-store-page-navigated');
|
|
570
|
+
})
|
|
571
|
+
.then(function()
|
|
572
|
+
{
|
|
573
|
+
return _Page.evaluate(function()
|
|
574
|
+
{
|
|
575
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
576
|
+
var appState = window._Pict.AppData.Bookshop;
|
|
577
|
+
return {
|
|
578
|
+
currentView: appState.CurrentView,
|
|
579
|
+
currentBook: appState.CurrentBook ? appState.CurrentBook.Title : null,
|
|
580
|
+
currentRoute: state.CurrentRoute,
|
|
581
|
+
topic: state.Topic,
|
|
582
|
+
currentPath: state.CurrentPath
|
|
583
|
+
};
|
|
584
|
+
});
|
|
585
|
+
})
|
|
586
|
+
.then(function(pResult)
|
|
587
|
+
{
|
|
588
|
+
Expect(pResult.currentView).to.equal('Store');
|
|
589
|
+
Expect(pResult.currentBook).to.be.a('string');
|
|
590
|
+
Expect(pResult.currentRoute).to.match(/^\/books\/store\/\d+$/);
|
|
591
|
+
Expect(pResult.topic).to.equal('BOOKSHOP-STORE');
|
|
592
|
+
Expect(pResult.currentPath).to.equal('store.md');
|
|
593
|
+
recordTestResult('Navigate to store page and help updates to store topic via route matching', true);
|
|
594
|
+
fDone();
|
|
595
|
+
})
|
|
596
|
+
.catch(function(pError) { recordTestResult('Navigate to store page and help updates to store topic via route matching', false, pError.message); fDone(pError); });
|
|
597
|
+
}
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
// ====================================================================
|
|
602
|
+
// Test 4: Open help on store page via ? button
|
|
603
|
+
// ====================================================================
|
|
604
|
+
test
|
|
605
|
+
(
|
|
606
|
+
'Help button on store page opens contextual help for the store',
|
|
607
|
+
function(fDone)
|
|
608
|
+
{
|
|
609
|
+
_Page.evaluate(function() { document.getElementById('Bookshop-Help-Store').click(); })
|
|
610
|
+
.then(function()
|
|
611
|
+
{
|
|
612
|
+
return _Page.waitForFunction(
|
|
613
|
+
'document.getElementById("Bookshop-Help-Panel") && document.getElementById("Bookshop-Help-Panel").classList.contains("visible")',
|
|
614
|
+
{ timeout: 5000 }
|
|
615
|
+
);
|
|
616
|
+
})
|
|
617
|
+
.then(function()
|
|
618
|
+
{
|
|
619
|
+
return captureScreenshot('04-store-help-via-question-mark');
|
|
620
|
+
})
|
|
621
|
+
.then(function()
|
|
622
|
+
{
|
|
623
|
+
return captureStateLog('04-store-help-via-question-mark');
|
|
624
|
+
})
|
|
625
|
+
.then(function()
|
|
626
|
+
{
|
|
627
|
+
return _Page.evaluate(function()
|
|
628
|
+
{
|
|
629
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
630
|
+
var body = document.getElementById('InlineDoc-Content-Body');
|
|
631
|
+
return {
|
|
632
|
+
panelVisible: document.getElementById('Bookshop-Help-Panel').classList.contains('visible'),
|
|
633
|
+
topic: state.Topic,
|
|
634
|
+
bodyHtml: body ? body.innerHTML.substring(0, 200) : ''
|
|
635
|
+
};
|
|
636
|
+
});
|
|
637
|
+
})
|
|
638
|
+
.then(function(pResult)
|
|
639
|
+
{
|
|
640
|
+
Expect(pResult.panelVisible).to.be.true;
|
|
641
|
+
Expect(pResult.topic).to.equal('BOOKSHOP-STORE');
|
|
642
|
+
Expect(pResult.bodyHtml.length).to.be.above(0);
|
|
643
|
+
recordTestResult('Help button on store page opens contextual help for the store', true);
|
|
644
|
+
fDone();
|
|
645
|
+
})
|
|
646
|
+
.catch(function(pError) { recordTestResult('Help button on store page opens contextual help for the store', false, pError.message); fDone(pError); });
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
// ====================================================================
|
|
652
|
+
// Test 5: Edit existing documentation — toggle to edit, modify, save
|
|
653
|
+
// ====================================================================
|
|
654
|
+
test
|
|
655
|
+
(
|
|
656
|
+
'Edit mode: toggle into edit, modify content, and save',
|
|
657
|
+
function(fDone)
|
|
658
|
+
{
|
|
659
|
+
_Page.evaluate(function()
|
|
660
|
+
{
|
|
661
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
662
|
+
return {
|
|
663
|
+
editEnabled: state.EditEnabled,
|
|
664
|
+
editing: state.Editing,
|
|
665
|
+
currentPath: state.CurrentPath
|
|
666
|
+
};
|
|
667
|
+
})
|
|
668
|
+
.then(function(pState)
|
|
669
|
+
{
|
|
670
|
+
Expect(pState.editEnabled).to.be.true;
|
|
671
|
+
Expect(pState.editing).to.be.false;
|
|
672
|
+
|
|
673
|
+
return _Page.evaluate(function() { document.getElementById('InlineDoc-Edit-Toggle').click(); });
|
|
674
|
+
})
|
|
675
|
+
.then(function()
|
|
676
|
+
{
|
|
677
|
+
return _Page.waitForSelector('#InlineDoc-Editor-Container', { timeout: 5000 });
|
|
678
|
+
})
|
|
679
|
+
.then(function()
|
|
680
|
+
{
|
|
681
|
+
return captureScreenshot('05a-edit-mode-textarea-open');
|
|
682
|
+
})
|
|
683
|
+
.then(function()
|
|
684
|
+
{
|
|
685
|
+
return captureStateLog('05a-edit-mode-textarea-open');
|
|
686
|
+
})
|
|
687
|
+
.then(function()
|
|
688
|
+
{
|
|
689
|
+
return _Page.evaluate(function()
|
|
690
|
+
{
|
|
691
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
692
|
+
var editorContainer = document.getElementById('InlineDoc-Editor-Container');
|
|
693
|
+
var saveBtn = document.getElementById('InlineDoc-Edit-Save');
|
|
694
|
+
var cancelBtn = document.getElementById('InlineDoc-Edit-Cancel');
|
|
695
|
+
var editBtn = document.getElementById('InlineDoc-Edit-Toggle');
|
|
696
|
+
return {
|
|
697
|
+
editing: state.Editing,
|
|
698
|
+
editingPath: state.EditingPath,
|
|
699
|
+
hasEditor: !!editorContainer,
|
|
700
|
+
contentLength: (state.EditorSegments && state.EditorSegments.length > 0) ? (state.EditorSegments[0].Content || '').length : 0,
|
|
701
|
+
saveBtnVisible: saveBtn ? saveBtn.style.display !== 'none' : false,
|
|
702
|
+
cancelBtnVisible: cancelBtn ? cancelBtn.style.display !== 'none' : false,
|
|
703
|
+
editBtnHidden: editBtn ? editBtn.style.display === 'none' : false
|
|
704
|
+
};
|
|
705
|
+
});
|
|
706
|
+
})
|
|
707
|
+
.then(function(pResult)
|
|
708
|
+
{
|
|
709
|
+
Expect(pResult.editing).to.be.true;
|
|
710
|
+
Expect(pResult.editingPath).to.equal('store.md');
|
|
711
|
+
Expect(pResult.hasEditor).to.be.true;
|
|
712
|
+
Expect(pResult.contentLength).to.be.above(0, 'Editor should contain markdown');
|
|
713
|
+
Expect(pResult.saveBtnVisible).to.be.true;
|
|
714
|
+
Expect(pResult.cancelBtnVisible).to.be.true;
|
|
715
|
+
Expect(pResult.editBtnHidden).to.be.true;
|
|
716
|
+
|
|
717
|
+
return _Page.evaluate(function()
|
|
718
|
+
{
|
|
719
|
+
var editorView = window._Pict.views['InlineDoc-MarkdownEditor'];
|
|
720
|
+
var newContent = '# Edited Store Help\n\nThis was edited by the browser test.\n\n## Browser Test Section\n\nNew content added.';
|
|
721
|
+
if (editorView && typeof editorView.setSegmentContent === 'function')
|
|
722
|
+
{
|
|
723
|
+
editorView.setSegmentContent(0, newContent);
|
|
724
|
+
}
|
|
725
|
+
window._Pict.AppData.InlineDocumentation.EditorSegments[0].Content = newContent;
|
|
726
|
+
return newContent.length;
|
|
727
|
+
});
|
|
728
|
+
})
|
|
729
|
+
.then(function(pNewLength)
|
|
730
|
+
{
|
|
731
|
+
Expect(pNewLength).to.be.above(0);
|
|
732
|
+
|
|
733
|
+
return _Page.evaluate(function() { document.getElementById('InlineDoc-Edit-Save').click(); });
|
|
734
|
+
})
|
|
735
|
+
.then(function()
|
|
736
|
+
{
|
|
737
|
+
return _Page.waitForFunction(
|
|
738
|
+
'!document.getElementById("InlineDoc-Editor-Container")',
|
|
739
|
+
{ timeout: 5000 }
|
|
740
|
+
);
|
|
741
|
+
})
|
|
742
|
+
.then(function()
|
|
743
|
+
{
|
|
744
|
+
return captureScreenshot('05b-edit-mode-saved');
|
|
745
|
+
})
|
|
746
|
+
.then(function()
|
|
747
|
+
{
|
|
748
|
+
return captureStateLog('05b-edit-mode-saved');
|
|
749
|
+
})
|
|
750
|
+
.then(function()
|
|
751
|
+
{
|
|
752
|
+
return _Page.evaluate(function()
|
|
753
|
+
{
|
|
754
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
755
|
+
var body = document.getElementById('InlineDoc-Content-Body');
|
|
756
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
757
|
+
var cache = provider._ContentCache['docs/store.md'];
|
|
758
|
+
return {
|
|
759
|
+
editing: state.Editing,
|
|
760
|
+
bodyContainsEdited: body ? body.innerHTML.indexOf('Edited Store Help') >= 0 : false,
|
|
761
|
+
bodyContainsNewSection: body ? body.innerHTML.indexOf('Browser Test Section') >= 0 : false,
|
|
762
|
+
cacheUpdated: cache ? cache.markdown.indexOf('Edited Store Help') >= 0 : false
|
|
763
|
+
};
|
|
764
|
+
});
|
|
765
|
+
})
|
|
766
|
+
.then(function(pResult)
|
|
767
|
+
{
|
|
768
|
+
Expect(pResult.editing).to.be.false;
|
|
769
|
+
Expect(pResult.bodyContainsEdited).to.be.true;
|
|
770
|
+
Expect(pResult.bodyContainsNewSection).to.be.true;
|
|
771
|
+
Expect(pResult.cacheUpdated).to.be.true;
|
|
772
|
+
recordTestResult('Edit mode: toggle into edit, modify content, and save', true);
|
|
773
|
+
fDone();
|
|
774
|
+
})
|
|
775
|
+
.catch(function(pError) { recordTestResult('Edit mode: toggle into edit, modify content, and save', false, pError.message); fDone(pError); });
|
|
776
|
+
}
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
// ====================================================================
|
|
781
|
+
// Test 6: Edit and Cancel — content should revert
|
|
782
|
+
// ====================================================================
|
|
783
|
+
test
|
|
784
|
+
(
|
|
785
|
+
'Edit mode: cancel reverts to original rendered content',
|
|
786
|
+
function(fDone)
|
|
787
|
+
{
|
|
788
|
+
_Page.evaluate(function() { document.getElementById('InlineDoc-Edit-Toggle').click(); })
|
|
789
|
+
.then(function()
|
|
790
|
+
{
|
|
791
|
+
return _Page.waitForSelector('#InlineDoc-Editor-Container', { timeout: 5000 });
|
|
792
|
+
})
|
|
793
|
+
.then(function()
|
|
794
|
+
{
|
|
795
|
+
return _Page.evaluate(function()
|
|
796
|
+
{
|
|
797
|
+
var editorView = window._Pict.views['InlineDoc-MarkdownEditor'];
|
|
798
|
+
var newContent = '# CANCELLED CHANGES\n\nThis should not appear.';
|
|
799
|
+
if (editorView && typeof editorView.setSegmentContent === 'function')
|
|
800
|
+
{
|
|
801
|
+
editorView.setSegmentContent(0, newContent);
|
|
802
|
+
}
|
|
803
|
+
window._Pict.AppData.InlineDocumentation.EditorSegments[0].Content = newContent;
|
|
804
|
+
});
|
|
805
|
+
})
|
|
806
|
+
.then(function()
|
|
807
|
+
{
|
|
808
|
+
return captureScreenshot('06a-edit-mode-before-cancel');
|
|
809
|
+
})
|
|
810
|
+
.then(function()
|
|
811
|
+
{
|
|
812
|
+
return _Page.evaluate(function() { document.getElementById('InlineDoc-Edit-Cancel').click(); });
|
|
813
|
+
})
|
|
814
|
+
.then(function()
|
|
815
|
+
{
|
|
816
|
+
return _Page.waitForFunction(
|
|
817
|
+
'!document.getElementById("InlineDoc-Editor-Container")',
|
|
818
|
+
{ timeout: 5000 }
|
|
819
|
+
);
|
|
820
|
+
})
|
|
821
|
+
.then(function()
|
|
822
|
+
{
|
|
823
|
+
return captureScreenshot('06b-edit-mode-after-cancel');
|
|
824
|
+
})
|
|
825
|
+
.then(function()
|
|
826
|
+
{
|
|
827
|
+
return captureStateLog('06-edit-cancel');
|
|
828
|
+
})
|
|
829
|
+
.then(function()
|
|
830
|
+
{
|
|
831
|
+
return _Page.evaluate(function()
|
|
832
|
+
{
|
|
833
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
834
|
+
var body = document.getElementById('InlineDoc-Content-Body');
|
|
835
|
+
return {
|
|
836
|
+
editing: state.Editing,
|
|
837
|
+
bodyDoesNotContainCancelled: body ? body.innerHTML.indexOf('CANCELLED CHANGES') < 0 : true,
|
|
838
|
+
bodyContainsSavedContent: body ? body.innerHTML.indexOf('Edited Store Help') >= 0 : false
|
|
839
|
+
};
|
|
840
|
+
});
|
|
841
|
+
})
|
|
842
|
+
.then(function(pResult)
|
|
843
|
+
{
|
|
844
|
+
Expect(pResult.editing).to.be.false;
|
|
845
|
+
Expect(pResult.bodyDoesNotContainCancelled).to.be.true;
|
|
846
|
+
Expect(pResult.bodyContainsSavedContent).to.be.true;
|
|
847
|
+
recordTestResult('Edit mode: cancel reverts to original rendered content', true);
|
|
848
|
+
fDone();
|
|
849
|
+
})
|
|
850
|
+
.catch(function(pError) { recordTestResult('Edit mode: cancel reverts to original rendered content', false, pError.message); fDone(pError); });
|
|
851
|
+
}
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
// ====================================================================
|
|
856
|
+
// Test 7: Navigate back to book list and verify help context switches
|
|
857
|
+
// ====================================================================
|
|
858
|
+
test
|
|
859
|
+
(
|
|
860
|
+
'Navigate back to book list and help context switches to book list topic',
|
|
861
|
+
function(fDone)
|
|
862
|
+
{
|
|
863
|
+
_Page.evaluate(function()
|
|
864
|
+
{
|
|
865
|
+
var appState = window._Pict.AppData.Bookshop;
|
|
866
|
+
if (appState.CurrentView !== 'Store')
|
|
867
|
+
{
|
|
868
|
+
window._Pict.PictApplication.showBook(1);
|
|
869
|
+
}
|
|
870
|
+
var panel = document.getElementById('Bookshop-Help-Panel');
|
|
871
|
+
if (panel && panel.classList.contains('visible'))
|
|
872
|
+
{
|
|
873
|
+
window._Pict.PictApplication.toggleHelp();
|
|
874
|
+
}
|
|
875
|
+
})
|
|
876
|
+
.then(function()
|
|
877
|
+
{
|
|
878
|
+
return _Page.waitForSelector('#Bookshop-Store-Back', { timeout: 5000 });
|
|
879
|
+
})
|
|
880
|
+
.then(function()
|
|
881
|
+
{
|
|
882
|
+
return _Page.evaluate(function() { document.getElementById('Bookshop-Store-Back').click(); });
|
|
883
|
+
})
|
|
884
|
+
.then(function()
|
|
885
|
+
{
|
|
886
|
+
return _Page.waitForSelector('.bookshop-book-card', { timeout: 5000 });
|
|
887
|
+
})
|
|
888
|
+
.then(function()
|
|
889
|
+
{
|
|
890
|
+
return captureScreenshot('07-navigated-back-to-book-list');
|
|
891
|
+
})
|
|
892
|
+
.then(function()
|
|
893
|
+
{
|
|
894
|
+
return captureStateLog('07-navigated-back-to-book-list');
|
|
895
|
+
})
|
|
896
|
+
.then(function()
|
|
897
|
+
{
|
|
898
|
+
return _Page.evaluate(function()
|
|
899
|
+
{
|
|
900
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
901
|
+
return {
|
|
902
|
+
currentRoute: state.CurrentRoute,
|
|
903
|
+
topic: state.Topic,
|
|
904
|
+
currentPath: state.CurrentPath
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
})
|
|
908
|
+
.then(function(pResult)
|
|
909
|
+
{
|
|
910
|
+
Expect(pResult.currentRoute).to.equal('/books');
|
|
911
|
+
Expect(pResult.topic).to.equal('BOOKSHOP-BOOKLIST');
|
|
912
|
+
Expect(pResult.currentPath).to.equal('book-list.md');
|
|
913
|
+
recordTestResult('Navigate back to book list and help context switches to book list topic', true);
|
|
914
|
+
fDone();
|
|
915
|
+
})
|
|
916
|
+
.catch(function(pError) { recordTestResult('Navigate back to book list and help context switches to book list topic', false, pError.message); fDone(pError); });
|
|
917
|
+
}
|
|
918
|
+
);
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
// ====================================================================
|
|
922
|
+
// Test 8: F1 keyboard shortcut toggles help
|
|
923
|
+
// ====================================================================
|
|
924
|
+
test
|
|
925
|
+
(
|
|
926
|
+
'F1 keyboard shortcut toggles help panel',
|
|
927
|
+
function(fDone)
|
|
928
|
+
{
|
|
929
|
+
_Page.evaluate(function()
|
|
930
|
+
{
|
|
931
|
+
var panel = document.getElementById('Bookshop-Help-Panel');
|
|
932
|
+
if (panel && panel.classList.contains('visible'))
|
|
933
|
+
{
|
|
934
|
+
window._Pict.PictApplication.toggleHelp();
|
|
935
|
+
}
|
|
936
|
+
return !document.getElementById('Bookshop-Help-Panel').classList.contains('visible');
|
|
937
|
+
})
|
|
938
|
+
.then(function(pIsClosed)
|
|
939
|
+
{
|
|
940
|
+
Expect(pIsClosed).to.be.true;
|
|
941
|
+
|
|
942
|
+
return _Page.keyboard.press('F1');
|
|
943
|
+
})
|
|
944
|
+
.then(function()
|
|
945
|
+
{
|
|
946
|
+
return _Page.waitForFunction(
|
|
947
|
+
'document.getElementById("Bookshop-Help-Panel").classList.contains("visible")',
|
|
948
|
+
{ timeout: 5000 }
|
|
949
|
+
);
|
|
950
|
+
})
|
|
951
|
+
.then(function()
|
|
952
|
+
{
|
|
953
|
+
return captureScreenshot('08a-f1-help-opened');
|
|
954
|
+
})
|
|
955
|
+
.then(function()
|
|
956
|
+
{
|
|
957
|
+
return _Page.keyboard.press('F1');
|
|
958
|
+
})
|
|
959
|
+
.then(function()
|
|
960
|
+
{
|
|
961
|
+
return _Page.waitForFunction(
|
|
962
|
+
'!document.getElementById("Bookshop-Help-Panel").classList.contains("visible")',
|
|
963
|
+
{ timeout: 5000 }
|
|
964
|
+
);
|
|
965
|
+
})
|
|
966
|
+
.then(function()
|
|
967
|
+
{
|
|
968
|
+
return captureScreenshot('08b-f1-help-closed');
|
|
969
|
+
})
|
|
970
|
+
.then(function()
|
|
971
|
+
{
|
|
972
|
+
return captureStateLog('08-f1-toggle');
|
|
973
|
+
})
|
|
974
|
+
.then(function()
|
|
975
|
+
{
|
|
976
|
+
return _Page.evaluate(function()
|
|
977
|
+
{
|
|
978
|
+
return !document.getElementById('Bookshop-Help-Panel').classList.contains('visible');
|
|
979
|
+
});
|
|
980
|
+
})
|
|
981
|
+
.then(function(pIsClosed)
|
|
982
|
+
{
|
|
983
|
+
Expect(pIsClosed).to.be.true;
|
|
984
|
+
recordTestResult('F1 keyboard shortcut toggles help panel', true);
|
|
985
|
+
fDone();
|
|
986
|
+
})
|
|
987
|
+
.catch(function(pError) { recordTestResult('F1 keyboard shortcut toggles help panel', false, pError.message); fDone(pError); });
|
|
988
|
+
}
|
|
989
|
+
);
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
// ====================================================================
|
|
993
|
+
// Test 9: Create a new documentation topic at runtime (not route-bound)
|
|
994
|
+
// ====================================================================
|
|
995
|
+
test
|
|
996
|
+
(
|
|
997
|
+
'Create a new topic at runtime that is not bound to any route',
|
|
998
|
+
function(fDone)
|
|
999
|
+
{
|
|
1000
|
+
_Page.evaluate(function()
|
|
1001
|
+
{
|
|
1002
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1003
|
+
|
|
1004
|
+
provider.addTopic('CUSTOM-HELP',
|
|
1005
|
+
{
|
|
1006
|
+
TopicHelpFilePath: 'custom-help.md',
|
|
1007
|
+
TopicTitle: 'Custom Runtime Help'
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
var url = 'docs/custom-help.md';
|
|
1011
|
+
var markdown = '# Custom Help Topic\\n\\nThis documentation was created at runtime.\\n\\n## Not Route Bound\\n\\nThis topic has no route association.';
|
|
1012
|
+
var html = provider._ContentProvider.parseMarkdown(markdown);
|
|
1013
|
+
provider._ContentCache[url] = { html: html, markdown: markdown };
|
|
1014
|
+
|
|
1015
|
+
provider.loadTopicDocument('CUSTOM-HELP');
|
|
1016
|
+
|
|
1017
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
1018
|
+
return {
|
|
1019
|
+
topicExists: !!state.Topics['CUSTOM-HELP'],
|
|
1020
|
+
topicTitle: state.Topics['CUSTOM-HELP'] ? state.Topics['CUSTOM-HELP'].TopicTitle : null,
|
|
1021
|
+
topicHasRoutes: !!(state.Topics['CUSTOM-HELP'] && state.Topics['CUSTOM-HELP'].Routes),
|
|
1022
|
+
activeTopic: state.Topic,
|
|
1023
|
+
currentPath: state.CurrentPath
|
|
1024
|
+
};
|
|
1025
|
+
})
|
|
1026
|
+
.then(function(pResult)
|
|
1027
|
+
{
|
|
1028
|
+
Expect(pResult.topicExists).to.be.true;
|
|
1029
|
+
Expect(pResult.topicTitle).to.equal('Custom Runtime Help');
|
|
1030
|
+
Expect(pResult.topicHasRoutes).to.be.false;
|
|
1031
|
+
Expect(pResult.activeTopic).to.equal('CUSTOM-HELP');
|
|
1032
|
+
Expect(pResult.currentPath).to.equal('custom-help.md');
|
|
1033
|
+
|
|
1034
|
+
return _Page.evaluate(function()
|
|
1035
|
+
{
|
|
1036
|
+
var panel = document.getElementById('Bookshop-Help-Panel');
|
|
1037
|
+
if (!panel.classList.contains('visible'))
|
|
1038
|
+
{
|
|
1039
|
+
window._Pict.PictApplication.toggleHelp();
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
})
|
|
1043
|
+
.then(function()
|
|
1044
|
+
{
|
|
1045
|
+
return _Page.waitForFunction(
|
|
1046
|
+
'document.getElementById("Bookshop-Help-Panel").classList.contains("visible")',
|
|
1047
|
+
{ timeout: 3000 }
|
|
1048
|
+
);
|
|
1049
|
+
})
|
|
1050
|
+
.then(function()
|
|
1051
|
+
{
|
|
1052
|
+
return captureScreenshot('09-runtime-topic-created');
|
|
1053
|
+
})
|
|
1054
|
+
.then(function()
|
|
1055
|
+
{
|
|
1056
|
+
return captureStateLog('09-runtime-topic-created');
|
|
1057
|
+
})
|
|
1058
|
+
.then(function()
|
|
1059
|
+
{
|
|
1060
|
+
return _Page.evaluate(function()
|
|
1061
|
+
{
|
|
1062
|
+
var body = document.getElementById('InlineDoc-Content-Body');
|
|
1063
|
+
return {
|
|
1064
|
+
hasCustomContent: body ? body.innerHTML.indexOf('Custom Help Topic') >= 0 : false,
|
|
1065
|
+
hasNotRouteBound: body ? body.innerHTML.indexOf('Not Route Bound') >= 0 : false
|
|
1066
|
+
};
|
|
1067
|
+
});
|
|
1068
|
+
})
|
|
1069
|
+
.then(function(pResult)
|
|
1070
|
+
{
|
|
1071
|
+
Expect(pResult.hasCustomContent).to.be.true;
|
|
1072
|
+
Expect(pResult.hasNotRouteBound).to.be.true;
|
|
1073
|
+
|
|
1074
|
+
return _Page.evaluate(function()
|
|
1075
|
+
{
|
|
1076
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1077
|
+
var match = provider.resolveHelpForRoute('/custom');
|
|
1078
|
+
return match;
|
|
1079
|
+
});
|
|
1080
|
+
})
|
|
1081
|
+
.then(function(pMatch)
|
|
1082
|
+
{
|
|
1083
|
+
Expect(pMatch).to.be.null;
|
|
1084
|
+
recordTestResult('Create a new topic at runtime that is not bound to any route', true);
|
|
1085
|
+
fDone();
|
|
1086
|
+
})
|
|
1087
|
+
.catch(function(pError) { recordTestResult('Create a new topic at runtime that is not bound to any route', false, pError.message); fDone(pError); });
|
|
1088
|
+
}
|
|
1089
|
+
);
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
// ====================================================================
|
|
1093
|
+
// Test 10: Bind a documentation entry to a route
|
|
1094
|
+
// ====================================================================
|
|
1095
|
+
test
|
|
1096
|
+
(
|
|
1097
|
+
'Bind an existing topic to a route and verify it resolves',
|
|
1098
|
+
function(fDone)
|
|
1099
|
+
{
|
|
1100
|
+
_Page.evaluate(function()
|
|
1101
|
+
{
|
|
1102
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1103
|
+
|
|
1104
|
+
provider.addRouteToTopic('CUSTOM-HELP', '/custom');
|
|
1105
|
+
provider.addRouteToTopic('CUSTOM-HELP', '/custom/*');
|
|
1106
|
+
|
|
1107
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
1108
|
+
var topic = state.Topics['CUSTOM-HELP'];
|
|
1109
|
+
|
|
1110
|
+
return {
|
|
1111
|
+
hasRoutes: Array.isArray(topic.Routes),
|
|
1112
|
+
routeCount: topic.Routes ? topic.Routes.length : 0,
|
|
1113
|
+
routes: topic.Routes || [],
|
|
1114
|
+
resolvedExact: provider.resolveHelpForRoute('/custom'),
|
|
1115
|
+
resolvedWildcard: provider.resolveHelpForRoute('/custom/sub/page'),
|
|
1116
|
+
resolvedUnrelated: provider.resolveHelpForRoute('/unrelated')
|
|
1117
|
+
};
|
|
1118
|
+
})
|
|
1119
|
+
.then(function(pResult)
|
|
1120
|
+
{
|
|
1121
|
+
Expect(pResult.hasRoutes).to.be.true;
|
|
1122
|
+
Expect(pResult.routeCount).to.equal(2);
|
|
1123
|
+
Expect(pResult.routes).to.include('/custom');
|
|
1124
|
+
Expect(pResult.routes).to.include('/custom/*');
|
|
1125
|
+
Expect(pResult.resolvedExact).to.equal('CUSTOM-HELP');
|
|
1126
|
+
Expect(pResult.resolvedWildcard).to.equal('CUSTOM-HELP');
|
|
1127
|
+
Expect(pResult.resolvedUnrelated).to.be.null;
|
|
1128
|
+
recordTestResult('Bind an existing topic to a route and verify it resolves', true);
|
|
1129
|
+
fDone();
|
|
1130
|
+
})
|
|
1131
|
+
.catch(function(pError) { recordTestResult('Bind an existing topic to a route and verify it resolves', false, pError.message); fDone(pError); });
|
|
1132
|
+
}
|
|
1133
|
+
);
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
// ====================================================================
|
|
1137
|
+
// Test 11: Multiple topics match the same route — getTopicsForRoute
|
|
1138
|
+
// ====================================================================
|
|
1139
|
+
test
|
|
1140
|
+
(
|
|
1141
|
+
'Multiple topics matching one route, user can navigate between them',
|
|
1142
|
+
function(fDone)
|
|
1143
|
+
{
|
|
1144
|
+
_Page.evaluate(function()
|
|
1145
|
+
{
|
|
1146
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1147
|
+
|
|
1148
|
+
provider.addTopic('BOOKSHOP-FAQ',
|
|
1149
|
+
{
|
|
1150
|
+
TopicHelpFilePath: 'faq.md',
|
|
1151
|
+
TopicTitle: 'Frequently Asked Questions',
|
|
1152
|
+
Routes: ['/books', '/books/*']
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
var markdown = '# Bookshop FAQ\\n\\nFrequently asked questions about the bookshop.\\n\\n## How do I buy a book?\\n\\nClick on a book card and then click Add to Cart.';
|
|
1156
|
+
var html = provider._ContentProvider.parseMarkdown(markdown);
|
|
1157
|
+
provider._ContentCache['docs/faq.md'] = { html: html, markdown: markdown };
|
|
1158
|
+
|
|
1159
|
+
var matches = provider.getTopicsForRoute('/books');
|
|
1160
|
+
|
|
1161
|
+
return {
|
|
1162
|
+
matchCount: matches.length,
|
|
1163
|
+
matchCodes: matches.map(function(m) { return m.TopicCode; }),
|
|
1164
|
+
firstMatch: matches.length > 0 ? matches[0].TopicCode : null,
|
|
1165
|
+
allHavePatterns: matches.every(function(m) { return m.Pattern && m.MatchLength > 0; })
|
|
1166
|
+
};
|
|
1167
|
+
})
|
|
1168
|
+
.then(function(pResult)
|
|
1169
|
+
{
|
|
1170
|
+
Expect(pResult.matchCount).to.be.at.least(2, 'Should have at least two topics matching /books');
|
|
1171
|
+
Expect(pResult.matchCodes).to.include('BOOKSHOP-BOOKLIST');
|
|
1172
|
+
Expect(pResult.matchCodes).to.include('BOOKSHOP-FAQ');
|
|
1173
|
+
Expect(pResult.allHavePatterns).to.be.true;
|
|
1174
|
+
|
|
1175
|
+
return _Page.evaluate(function()
|
|
1176
|
+
{
|
|
1177
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1178
|
+
provider.loadTopicDocument('BOOKSHOP-BOOKLIST');
|
|
1179
|
+
return true;
|
|
1180
|
+
});
|
|
1181
|
+
})
|
|
1182
|
+
.then(function()
|
|
1183
|
+
{
|
|
1184
|
+
return _Page.waitForFunction(
|
|
1185
|
+
'document.getElementById("InlineDoc-Content-Body") && document.getElementById("InlineDoc-Content-Body").innerHTML.indexOf("Book Catalog") >= 0',
|
|
1186
|
+
{ timeout: 5000 }
|
|
1187
|
+
);
|
|
1188
|
+
})
|
|
1189
|
+
.then(function()
|
|
1190
|
+
{
|
|
1191
|
+
return captureScreenshot('11a-multi-match-booklist-topic');
|
|
1192
|
+
})
|
|
1193
|
+
.then(function()
|
|
1194
|
+
{
|
|
1195
|
+
return _Page.evaluate(function()
|
|
1196
|
+
{
|
|
1197
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1198
|
+
provider.loadTopicDocument('BOOKSHOP-FAQ');
|
|
1199
|
+
return true;
|
|
1200
|
+
});
|
|
1201
|
+
})
|
|
1202
|
+
.then(function()
|
|
1203
|
+
{
|
|
1204
|
+
return _Page.waitForFunction(
|
|
1205
|
+
'document.getElementById("InlineDoc-Content-Body") && document.getElementById("InlineDoc-Content-Body").innerHTML.indexOf("Bookshop FAQ") >= 0',
|
|
1206
|
+
{ timeout: 5000 }
|
|
1207
|
+
);
|
|
1208
|
+
})
|
|
1209
|
+
.then(function()
|
|
1210
|
+
{
|
|
1211
|
+
return captureScreenshot('11b-multi-match-faq-topic');
|
|
1212
|
+
})
|
|
1213
|
+
.then(function()
|
|
1214
|
+
{
|
|
1215
|
+
return captureStateLog('11-multi-match-navigate');
|
|
1216
|
+
})
|
|
1217
|
+
.then(function()
|
|
1218
|
+
{
|
|
1219
|
+
return _Page.evaluate(function()
|
|
1220
|
+
{
|
|
1221
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
1222
|
+
var body = document.getElementById('InlineDoc-Content-Body');
|
|
1223
|
+
return {
|
|
1224
|
+
topic: state.Topic,
|
|
1225
|
+
currentPath: state.CurrentPath,
|
|
1226
|
+
hasFaqContent: body ? body.innerHTML.indexOf('Bookshop FAQ') >= 0 : false,
|
|
1227
|
+
hasHowToBuy: body ? body.innerHTML.indexOf('How do I buy a book') >= 0 : false
|
|
1228
|
+
};
|
|
1229
|
+
});
|
|
1230
|
+
})
|
|
1231
|
+
.then(function(pResult)
|
|
1232
|
+
{
|
|
1233
|
+
Expect(pResult.topic).to.equal('BOOKSHOP-FAQ');
|
|
1234
|
+
Expect(pResult.currentPath).to.equal('faq.md');
|
|
1235
|
+
Expect(pResult.hasFaqContent).to.be.true;
|
|
1236
|
+
Expect(pResult.hasHowToBuy).to.be.true;
|
|
1237
|
+
|
|
1238
|
+
// Switch back to confirm toggle works
|
|
1239
|
+
return _Page.evaluate(function()
|
|
1240
|
+
{
|
|
1241
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1242
|
+
provider.loadTopicDocument('BOOKSHOP-BOOKLIST');
|
|
1243
|
+
return true;
|
|
1244
|
+
});
|
|
1245
|
+
})
|
|
1246
|
+
.then(function()
|
|
1247
|
+
{
|
|
1248
|
+
return _Page.waitForFunction(
|
|
1249
|
+
'document.getElementById("InlineDoc-Content-Body") && document.getElementById("InlineDoc-Content-Body").innerHTML.indexOf("Book Catalog") >= 0',
|
|
1250
|
+
{ timeout: 5000 }
|
|
1251
|
+
);
|
|
1252
|
+
})
|
|
1253
|
+
.then(function()
|
|
1254
|
+
{
|
|
1255
|
+
return captureScreenshot('11c-multi-match-back-to-booklist');
|
|
1256
|
+
})
|
|
1257
|
+
.then(function()
|
|
1258
|
+
{
|
|
1259
|
+
return _Page.evaluate(function()
|
|
1260
|
+
{
|
|
1261
|
+
var state = window._Pict.AppData.InlineDocumentation;
|
|
1262
|
+
return { topic: state.Topic, currentPath: state.CurrentPath };
|
|
1263
|
+
});
|
|
1264
|
+
})
|
|
1265
|
+
.then(function(pResult)
|
|
1266
|
+
{
|
|
1267
|
+
Expect(pResult.topic).to.equal('BOOKSHOP-BOOKLIST');
|
|
1268
|
+
Expect(pResult.currentPath).to.equal('book-list.md');
|
|
1269
|
+
recordTestResult('Multiple topics matching one route, user can navigate between them', true);
|
|
1270
|
+
fDone();
|
|
1271
|
+
})
|
|
1272
|
+
.catch(function(pError) { recordTestResult('Multiple topics matching one route, user can navigate between them', false, pError.message); fDone(pError); });
|
|
1273
|
+
}
|
|
1274
|
+
);
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
// ====================================================================
|
|
1278
|
+
// Test 12: getTopicsForRoute on wildcard-heavy routes
|
|
1279
|
+
// ====================================================================
|
|
1280
|
+
test
|
|
1281
|
+
(
|
|
1282
|
+
'getTopicsForRoute returns sorted matches with wildcards',
|
|
1283
|
+
function(fDone)
|
|
1284
|
+
{
|
|
1285
|
+
_Page.evaluate(function()
|
|
1286
|
+
{
|
|
1287
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1288
|
+
|
|
1289
|
+
var matches = provider.getTopicsForRoute('/books/store/5');
|
|
1290
|
+
return {
|
|
1291
|
+
matchCount: matches.length,
|
|
1292
|
+
matchCodes: matches.map(function(m) { return m.TopicCode; }),
|
|
1293
|
+
firstMatch: matches.length > 0 ? matches[0].TopicCode : null,
|
|
1294
|
+
firstMatchLength: matches.length > 0 ? matches[0].MatchLength : 0,
|
|
1295
|
+
lastMatchLength: matches.length > 0 ? matches[matches.length - 1].MatchLength : 0
|
|
1296
|
+
};
|
|
1297
|
+
})
|
|
1298
|
+
.then(function(pResult)
|
|
1299
|
+
{
|
|
1300
|
+
Expect(pResult.matchCount).to.be.at.least(2, 'Should match BOOKSHOP-STORE and BOOKSHOP-FAQ');
|
|
1301
|
+
Expect(pResult.matchCodes).to.include('BOOKSHOP-STORE');
|
|
1302
|
+
Expect(pResult.matchCodes).to.include('BOOKSHOP-FAQ');
|
|
1303
|
+
Expect(pResult.firstMatch).to.equal('BOOKSHOP-STORE', 'BOOKSHOP-STORE should be first (longer prefix)');
|
|
1304
|
+
Expect(pResult.firstMatchLength).to.be.at.least(pResult.lastMatchLength, 'Results should be sorted by match length desc');
|
|
1305
|
+
recordTestResult('getTopicsForRoute returns sorted matches with wildcards', true);
|
|
1306
|
+
fDone();
|
|
1307
|
+
})
|
|
1308
|
+
.catch(function(pError) { recordTestResult('getTopicsForRoute returns sorted matches with wildcards', false, pError.message); fDone(pError); });
|
|
1309
|
+
}
|
|
1310
|
+
);
|
|
1311
|
+
|
|
1312
|
+
|
|
1313
|
+
// ====================================================================
|
|
1314
|
+
// Test 13: Edit the newly created runtime topic
|
|
1315
|
+
// ====================================================================
|
|
1316
|
+
test
|
|
1317
|
+
(
|
|
1318
|
+
'Edit the runtime-created topic content',
|
|
1319
|
+
function(fDone)
|
|
1320
|
+
{
|
|
1321
|
+
_Page.evaluate(function()
|
|
1322
|
+
{
|
|
1323
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1324
|
+
provider.loadTopicDocument('CUSTOM-HELP');
|
|
1325
|
+
var panel = document.getElementById('Bookshop-Help-Panel');
|
|
1326
|
+
if (!panel.classList.contains('visible'))
|
|
1327
|
+
{
|
|
1328
|
+
window._Pict.PictApplication.toggleHelp();
|
|
1329
|
+
}
|
|
1330
|
+
return true;
|
|
1331
|
+
})
|
|
1332
|
+
.then(function()
|
|
1333
|
+
{
|
|
1334
|
+
return _Page.waitForFunction(
|
|
1335
|
+
'document.getElementById("InlineDoc-Content-Body") && document.getElementById("InlineDoc-Content-Body").innerHTML.indexOf("Custom Help Topic") >= 0',
|
|
1336
|
+
{ timeout: 5000 }
|
|
1337
|
+
);
|
|
1338
|
+
})
|
|
1339
|
+
.then(function()
|
|
1340
|
+
{
|
|
1341
|
+
return _Page.evaluate(function() { document.getElementById('InlineDoc-Edit-Toggle').click(); });
|
|
1342
|
+
})
|
|
1343
|
+
.then(function()
|
|
1344
|
+
{
|
|
1345
|
+
return _Page.waitForSelector('#InlineDoc-Editor-Container', { timeout: 5000 });
|
|
1346
|
+
})
|
|
1347
|
+
.then(function()
|
|
1348
|
+
{
|
|
1349
|
+
return captureScreenshot('13a-edit-runtime-topic');
|
|
1350
|
+
})
|
|
1351
|
+
.then(function()
|
|
1352
|
+
{
|
|
1353
|
+
return _Page.evaluate(function()
|
|
1354
|
+
{
|
|
1355
|
+
var editorView = window._Pict.views['InlineDoc-MarkdownEditor'];
|
|
1356
|
+
var newContent = '# Updated Custom Help\n\nThis topic was edited after being created at runtime.\n\n## Now With Route Binding\n\nBound to /custom.';
|
|
1357
|
+
if (editorView && typeof editorView.setSegmentContent === 'function')
|
|
1358
|
+
{
|
|
1359
|
+
editorView.setSegmentContent(0, newContent);
|
|
1360
|
+
}
|
|
1361
|
+
window._Pict.AppData.InlineDocumentation.EditorSegments[0].Content = newContent;
|
|
1362
|
+
});
|
|
1363
|
+
})
|
|
1364
|
+
.then(function()
|
|
1365
|
+
{
|
|
1366
|
+
return _Page.evaluate(function() { document.getElementById('InlineDoc-Edit-Save').click(); });
|
|
1367
|
+
})
|
|
1368
|
+
.then(function()
|
|
1369
|
+
{
|
|
1370
|
+
return _Page.waitForFunction(
|
|
1371
|
+
'!document.getElementById("InlineDoc-Editor-Container")',
|
|
1372
|
+
{ timeout: 5000 }
|
|
1373
|
+
);
|
|
1374
|
+
})
|
|
1375
|
+
.then(function()
|
|
1376
|
+
{
|
|
1377
|
+
return captureScreenshot('13b-edit-runtime-topic-saved');
|
|
1378
|
+
})
|
|
1379
|
+
.then(function()
|
|
1380
|
+
{
|
|
1381
|
+
return captureStateLog('13-edit-runtime-topic');
|
|
1382
|
+
})
|
|
1383
|
+
.then(function()
|
|
1384
|
+
{
|
|
1385
|
+
return _Page.evaluate(function()
|
|
1386
|
+
{
|
|
1387
|
+
var body = document.getElementById('InlineDoc-Content-Body');
|
|
1388
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1389
|
+
var cache = provider._ContentCache['docs/custom-help.md'];
|
|
1390
|
+
return {
|
|
1391
|
+
hasUpdatedTitle: body ? body.innerHTML.indexOf('Updated Custom Help') >= 0 : false,
|
|
1392
|
+
cacheMarkdownUpdated: cache ? cache.markdown.indexOf('Updated Custom Help') >= 0 : false
|
|
1393
|
+
};
|
|
1394
|
+
});
|
|
1395
|
+
})
|
|
1396
|
+
.then(function(pResult)
|
|
1397
|
+
{
|
|
1398
|
+
Expect(pResult.hasUpdatedTitle).to.be.true;
|
|
1399
|
+
Expect(pResult.cacheMarkdownUpdated).to.be.true;
|
|
1400
|
+
recordTestResult('Edit the runtime-created topic content', true);
|
|
1401
|
+
fDone();
|
|
1402
|
+
})
|
|
1403
|
+
.catch(function(pError) { recordTestResult('Edit the runtime-created topic content', false, pError.message); fDone(pError); });
|
|
1404
|
+
}
|
|
1405
|
+
);
|
|
1406
|
+
|
|
1407
|
+
|
|
1408
|
+
// ====================================================================
|
|
1409
|
+
// Test 14: Verify edit disabled hides edit toolbar
|
|
1410
|
+
// ====================================================================
|
|
1411
|
+
test
|
|
1412
|
+
(
|
|
1413
|
+
'Disabling edit mode hides the edit toolbar',
|
|
1414
|
+
function(fDone)
|
|
1415
|
+
{
|
|
1416
|
+
_Page.evaluate(function()
|
|
1417
|
+
{
|
|
1418
|
+
var provider = window._Pict.providers['Pict-InlineDocumentation'];
|
|
1419
|
+
|
|
1420
|
+
provider.setEditEnabled(false);
|
|
1421
|
+
|
|
1422
|
+
var toolbar = document.getElementById('InlineDoc-Edit-Toolbar');
|
|
1423
|
+
var isHidden = toolbar ? !toolbar.classList.contains('visible') : true;
|
|
1424
|
+
|
|
1425
|
+
provider.setEditEnabled(true);
|
|
1426
|
+
|
|
1427
|
+
return { toolbarHiddenWhenDisabled: isHidden };
|
|
1428
|
+
})
|
|
1429
|
+
.then(function(pResult)
|
|
1430
|
+
{
|
|
1431
|
+
Expect(pResult.toolbarHiddenWhenDisabled).to.be.true;
|
|
1432
|
+
recordTestResult('Disabling edit mode hides the edit toolbar', true);
|
|
1433
|
+
|
|
1434
|
+
// Final screenshot
|
|
1435
|
+
return captureScreenshot('14-final-state');
|
|
1436
|
+
})
|
|
1437
|
+
.then(function()
|
|
1438
|
+
{
|
|
1439
|
+
return captureStateLog('14-final-state');
|
|
1440
|
+
})
|
|
1441
|
+
.then(function()
|
|
1442
|
+
{
|
|
1443
|
+
fDone();
|
|
1444
|
+
})
|
|
1445
|
+
.catch(function(pError) { recordTestResult('Disabling edit mode hides the edit toolbar', false, pError.message); fDone(pError); });
|
|
1446
|
+
}
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
);
|