1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
import {ChildProcess, spawn} from 'child_process';
import {assert, driver, IMochaServer, Key, useServer} from 'mocha-webdriver';
import fetch from 'node-fetch';
// tslint:disable:no-console
class MkdocsServer implements IMochaServer {
public _proc?: ChildProcess;
private _exitPromise: Promise<number|string> = Promise.resolve("not-started");
public async start() {
this._proc = spawn(process.env.MKDOCS_BINARY || 'env/bin/mkdocs', ['serve', '-a', 'localhost:8000'],
{cwd: '..', stdio: 'inherit'});
this._exitPromise = exitPromise(this._proc);
await this.waitServerReady(20000);
}
public async stop() {
if (this._proc) {
this._proc.kill('SIGINT');
const res = await this._exitPromise;
if (res !== 0) { console.log("ERROR: could not run mkdocs: %s", res); }
}
}
/**
* Wait for the server to be up and responsitve, for up to `ms` milliseconds.
*/
public async waitServerReady(ms: number): Promise<void> {
await driver.wait(() => Promise.race([
this.isServerReady(),
this._exitPromise.then(() => { throw new Error("Server exited while waiting for it"); }),
]), ms);
}
public isServerReady(): Promise<boolean> {
return fetch(this.getHost()).then((r) => r.ok).catch(() => false);
}
public getHost() {
return "http://localhost:8000";
}
}
describe('mkdocs_windmill', () => {
const server = new MkdocsServer();
useServer(server);
before(async function() {
this.timeout(60000); // Set a longer default timeout.
await driver.get(server.getHost());
});
beforeEach(async () => {
await driver.switchTo().defaultContent();
});
it('should load the homepage', async () => {
// Menu has links for items, including the first one.
assert.equal(await driver.findContent('.wm-toc-pane .wm-toc-text', /Usage/).getTagName(), 'a');
assert.equal(await driver.findContent('.wm-toc-pane .wm-toc-text', /Cust/).getTagName(), 'a');
// In certain configurations, the first page isn't selected until clicked in
// (this could be fixed, but for now we just test that it works once clicked).
await driver.findContent('.wm-toc-pane .wm-article-link', /Cust/).click();
await driver.findContent('.wm-toc-pane .wm-article-link', /Usage/).click();
assert.equal(await driver.find('.wm-current').getText(), 'Usage');
// The page table-of-contents is expanded in the menu.
assert.includeMembers(await driver.findAll('.wm-page-toc .wm-article-link',
(el) => el.getText()), ['About', 'Installation']);
// Check that homepage is visible in iframe
await driver.switchTo().frame(driver.find('.wm-article'));
assert.equal(await driver.find('h1').getText(), 'Windmill theme');
});
it('should go through all pages with nav links', async function() {
await driver.switchTo().frame(driver.find('.wm-article'));
const next = () => driver.findContent('.wm-article-nav a', /Next/).click();
const prev = () => driver.findContent('.wm-article-nav a', /Previous/).click();
await next();
assert.equal(await driver.find('h1').getText(), 'Customization');
await next();
assert.equal(await driver.find('h1').getText(), 'Heading A');
await next();
assert.equal(await driver.find('h1').getText(), 'Heading A1');
await next();
assert.equal(await driver.find('h1').getText(), 'Heading A2');
await prev();
assert.equal(await driver.find('h1').getText(), 'Heading A1');
await prev();
assert.equal(await driver.find('h1').getText(), 'Heading A');
await prev();
assert.equal(await driver.find('h1').getText(), 'Customization');
await prev();
assert.equal(await driver.find('h1').getText(), 'Windmill theme');
});
it('should follow links in search dropdown', async function() {
await driver.find('#mkdocs-search-query').doSendKeys('extra');
await assert.includeMembers(await driver.findAll('#mkdocs-search-results .search-title',
(el) => el.getText()), ['Extra configuration options', 'Usage']);
await driver.findContent('#mkdocs-search-results .search-title', /Extra conf/).click();
await driver.switchTo().frame(driver.find('.wm-article'));
assert.match(await driver.find('h1').getText(), /Customization/);
await driver.switchTo().defaultContent();
await driver.navigate().back();
await driver.find('#mkdocs-search-query').doClick();
await driver.findContent('#mkdocs-search-results .search-title', /Usage/).click();
await driver.switchTo().frame(driver.find('.wm-article'));
assert.match(await driver.find('h1').getText(), /Windmill theme/);
await driver.switchTo().defaultContent();
});
it('should follow links in search results page', async function() {
await driver.find('#mkdocs-search-query').doClear().doSendKeys('extra', Key.ENTER);
// Switch to the iframe with results. The actual elements to interact with are similar to the
// case above, because the search code is actually reused.
await driver.switchTo().frame(driver.find('.wm-article'));
await assert.includeMembers(await driver.findAll('#mkdocs-search-results .search-title',
(el) => el.getText()), ['Extra configuration options', 'Usage']);
await driver.findContent('#mkdocs-search-results .search-title', /Extra conf/).click();
assert.match(await driver.find('h1').getText(), /Customization/);
await driver.switchTo().defaultContent();
await driver.navigate().back();
await driver.find('#mkdocs-search-query').doClick().doSendKeys(Key.ENTER);
await driver.switchTo().frame(driver.find('.wm-article'));
await driver.findContent('#mkdocs-search-results .search-title', /Usage/).click();
assert.match(await driver.find('h1').getText(), /Windmill theme/);
});
});
export function exitPromise(child: ChildProcess): Promise<number|string> {
return new Promise((resolve, reject) => {
// Called if process could not be spawned, or could not be killed(!), or sending a message failed.
child.on('error', reject);
child.on('exit', (code: number, signal: string) => resolve(signal || code));
});
}
|