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