minor refactors
[squeep-html-template-helper] / test / lib / template-helper.js
1 /* eslint-env mocha */
2 'use strict';
3
4 const assert = require('assert');
5 const th = require('../../lib/template-helper');
6 const stubLogger = require('../stub-logger');
7
8 const { makeHtmlLint } = require('../lint-html');
9 const { HtmlValidate } = require('html-validate');
10 const htmlValidate = new HtmlValidate();
11 const htmlLint = makeHtmlLint(stubLogger, htmlValidate);
12
13 describe('Template Helper', function () {
14 let ctx, options, pagePathLevel;
15
16 beforeEach(function () {
17 pagePathLevel = 2;
18 ctx = {};
19 options = {};
20 });
21
22 describe('initContext', function () {
23 it('covers', function () {
24 th.initContext(ctx);
25 assert(ctx.errors);
26 assert(ctx.notifications);
27 assert(Array.isArray(ctx.errors));
28 assert(Array.isArray(ctx.notifications));
29 });
30 }); // initContext
31
32 describe('dateOrNot', function () {
33 let date, otherwise;
34 beforeEach(function () {
35 date = new Date();
36 otherwise = 'otherwise';
37 });
38 it('covers', function () {
39 const result = th.dateOrNot(date, otherwise);
40 assert.strictEqual(result, date.toString());
41 });
42 it('covers no date', function () {
43 date = undefined;
44 const result = th.dateOrNot(date, otherwise);
45 assert.strictEqual(result, otherwise);
46 });
47 it('covers ms', function () {
48 const result = th.dateOrNot(date.getTime(), otherwise);
49 assert.strictEqual(result, date.toString());
50 });
51 it('covers naught', function () {
52 const result = th.dateOrNot(0, otherwise);
53 assert.strictEqual(result, otherwise);
54 });
55 it('covers the infinite', function () {
56 const result = th.dateOrNot(-Infinity, otherwise);
57 assert.strictEqual(result, otherwise);
58 });
59 }); // dateOrNot
60
61 describe('dateFormat', function () {
62 it('renders otherwise', function () {
63 const expected = 'otherwise';
64 const result = th.dateFormat(undefined, undefined, undefined, expected);
65 assert.strictEqual(result, expected);
66 });
67 it('handles invalid date', function () {
68 const expected = 'otherwise';
69 const result = th.dateFormat(new Date('bad date'), undefined, undefined, expected);
70 assert.strictEqual(result, expected);
71 });
72 it('renders Infinity', function () {
73 const expected = 'end of time';
74 const result = th.dateFormat(Infinity, expected);
75 assert.strictEqual(result, expected);
76 });
77 it('renders -Infinity', function () {
78 const expected = 'beginning of time';
79 const result = th.dateFormat(-Infinity, undefined, expected);
80 assert.strictEqual(result, expected);
81 });
82 it('renders a Date', function () {
83 const expected = 'Mar 27, 2022, 3:28:05 PM PDT';
84 const result = th.dateFormat(new Date('2022-03-27T22:28:05.049Z'));
85 assert.strictEqual(result, expected);
86 });
87 it('renders a timestamp', function () {
88 const expected = 'Mar 27, 2022, 3:28:05 PM PDT';
89 const result = th.dateFormat(1648420085049);
90 assert.strictEqual(result, expected);
91 });
92 }); // dateFormat
93
94 describe('timeElement', function () {
95 it('renders a date', function () {
96 const when = new Date('2022-09-11T21:17:56.872Z');
97 const expected = '<time datetime="2022-09-11T21:17:56.872Z">Sep 11, 2022, 2:17:56 PM PDT</time>';
98 const result = th.timeElement(when);
99 assert.strictEqual(result, expected);
100 });
101 it('covers title', function () {
102 const when = new Date('2022-09-11T21:17:56.872Z');
103 const expected = '<time title="a date" datetime="2022-09-11T21:17:56.872Z">Sep 11, 2022, 2:17:56 PM PDT</time>';
104 const result = th.timeElement(when, { title: 'a date' });
105 assert.strictEqual(result, expected);
106 });
107 it('covers other', function () {
108 const expected = '<time>Mar 27, 2022, 3:28:05 PM PDT</time>';
109 const result = th.timeElement(1648420085049);
110 assert.strictEqual(result, expected);
111 });
112 }); // timeElement
113
114 describe('secondsToPeriod', function () {
115 it('covers seconds', function () {
116 const result = th.secondsToPeriod(45);
117 assert.strictEqual(result, '45 seconds');
118 });
119 it('covers minutes', function () {
120 const result = th.secondsToPeriod(105);
121 assert.strictEqual(result, '1 minute 45 seconds');
122 });
123 it('covers hours', function () {
124 const result = th.secondsToPeriod(3705);
125 assert.strictEqual(result, '1 hour 1 minute 45 seconds');
126 });
127 it('covers days', function () {
128 const result = th.secondsToPeriod(90105);
129 assert.strictEqual(result, '1 day 1 hour 1 minute 45 seconds');
130 });
131 it('covers months', function () {
132 const result = th.secondsToPeriod(5274105);
133 assert.strictEqual(result, '2 months 1 day 1 hour 1 minute 45 seconds');
134 });
135 }); // secondsToPeriod
136
137 describe('htmlHead', function () {
138 it('covers', function () {
139 const result = th.htmlHead(pagePathLevel, ctx, options);
140 assert(result);
141 });
142 it('covers elements', function () {
143 options.headElements = [ '<div>foop</div>', '<div>poof</div>' ];
144 const result = th.htmlHead(pagePathLevel, ctx, options);
145 assert(result);
146 });
147 it('covers onLoad', function () {
148 options.onload = 'onLoadFn';
149 const result = th.htmlHead(pagePathLevel, ctx, options);
150 assert(result);
151 });
152 }); // htmlHead
153
154 describe('renderNavLink', function () {
155 let nav;
156 beforeEach(function () {
157 nav = {
158 href: 'https://example.com/',
159 text: 'example',
160 };
161 });
162 it('covers no class', function () {
163 const result = th.renderNavLink(nav);
164 assert(result);
165 });
166 it('covers class', function () {
167 nav.class = 'foo bar';
168 const result = th.renderNavLink(nav);
169 assert(result);
170 });
171 }); // renderNavLink
172
173 describe('elementAttributes', function () {
174 it('covers', function () {
175 const attributes = {
176 class: 'foo bar',
177 disabled: '',
178 };
179 const result = th.elementAttributes(attributes);
180 assert.strictEqual(result, ' class="foo bar" disabled');
181 });
182 }); // elementAttributes
183
184 describe('OL', function () {
185 it('covers', function () {
186 const result = th.OL(['item', 'another item'], 1, { class: 'class' }, (item) => ({ class: `${item}-class` }));
187 assert(result);
188 });
189 it('covers defaults', function () {
190 const result = th.OL([]);
191 assert(result);
192 });
193 }); // OL
194
195 describe('UL', function () {
196 it('covers', function () {
197 const result = th.UL(['item', 'another item'], 1, { class: 'class' }, (item) => ({ class: `${item}-class` }));
198 assert(result);
199 });
200 it('covers defaults', function () {
201 const result = th.UL([]);
202 assert(result);
203 });
204 }); // UL
205
206 describe('LI', function () {
207 it('covers defaults', function () {
208 const result = th.LI('item');
209 assert(result);
210 });
211 }); // LI
212
213 describe('indented', function () {
214 it('covers', function () {
215 const result = th.indented(2, ['foo', 'bar']);
216 result.forEach((r) => assert(r.startsWith('\t\t')));
217 });
218 }); // indented
219
220 describe('htmlBody', function () {
221 it('covers no main', function () {
222 const result = th.htmlBody(pagePathLevel, ctx, options);
223 assert(result);
224 });
225 }); // htmlBody
226
227 describe('htmlHeader', function () {
228 it('covers no links', function () {
229 const result = th.htmlHeader(pagePathLevel, ctx, options);
230 assert(result);
231 });
232 it('covers links and logo', function () {
233 options.navLinks = [
234 {
235 href: 'https://exmaple.com/',
236 text: 'example',
237 },
238 ];
239 options.logoUrl = '/static/logo.svg';
240 const result = th.htmlHeader(pagePathLevel, ctx, options);
241 assert(result);
242 });
243 }); // htmlHeader
244
245 describe('htmlFooter', function () {
246 it('covers', function () {
247 options.footerEntries = [
248 '<div>foop</div>',
249 '<div>blah</div>',
250 ]
251 const result = th.htmlFooter(ctx, options);
252 assert(result);
253 });
254 it('covers default', function () {
255 const result = th.htmlFooter(ctx, options);
256 assert(!result);
257 });
258 }); // htmlFooter
259
260 describe('htmlMessages', function () {
261 it('covers', function () {
262 ctx.errors = ['an error'];
263 ctx.notifications = ['a notification'];
264 options.errorHeading = 'Errors';
265 options.notificationHeading = 'Notices';
266 options.errorContent = ['<p>Message about errors.</p>'];
267 options.notificationContent = ['<p>Message about notifications.</p>'];
268 const result = th.htmlMessages(ctx, options);
269 assert(result);
270 });
271 }); // htmlMessages
272
273 describe('htmlPage', function () {
274 let main;
275 beforeEach(function () {
276 th.initContext(ctx);
277 ctx.errors.push('an error');
278 ctx.notifications.push('a notice');
279 options.headElements = ['<link rel="author" href="https://example.com/">'];
280 options.pageTitle = 'Test Page';
281 main = [
282 th.UL(['an item', 'another item']),
283 th.timeElement(new Date(), { title: 'now' }),
284 ];
285 });
286 it('covers', async function () {
287 this.slow(1000); // First invocation of htmlLint takes some time.
288 const result = th.htmlPage(pagePathLevel, ctx, options, main);
289 await htmlLint(result);
290 assert(result);
291 });
292 it('covers defaults', async function () {
293 const result = th.htmlPage(pagePathLevel, ctx, options, main);
294 await htmlLint(result);
295 assert(result);
296 });
297 it('covers user', async function () {
298 ctx.session = {
299 authenticatedProfile: 'https://user.example.com/',
300 };
301 const result = th.htmlPage(pagePathLevel, ctx, options, main);
302 await htmlLint(result);
303 assert(result);
304 });
305 it('covers user at root path', async function () {
306 ctx.session = {
307 authenticatedIdentifier: 'user',
308 };
309 pagePathLevel = 0;
310 const result = th.htmlPage(pagePathLevel, ctx, options, main);
311 await htmlLint(result);
312 assert(result);
313 });
314 it('covers logout redirect', async function () {
315 ctx.session = {
316 authenticatedIdentifier: 'user',
317 };
318 ctx.url = 'https://app.example.com/this_page';
319 const result = th.htmlPage(pagePathLevel, ctx, options, main);
320 await htmlLint(result);
321 assert(result);
322 });
323 it('covers existing navLinks', async function () {
324 ctx.session = {
325 authenticatedIdentifier: 'user',
326 };
327 options.navLinks = [{
328 text: 'link',
329 href: 'link',
330 }];
331 const result = th.htmlPage(pagePathLevel, ctx, options);
332 await htmlLint(result);
333 assert(result);
334 });
335 }); // htmlPage
336
337 });