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