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