cleanup and rearrangement
[squeep-lazy-property] / test / lazy.js
1 /* eslint-env mocha */
2 /* eslint-disable capitalized-comments */
3 /* eslint-disable security/detect-object-injection */
4 'use strict';
5
6 const assert = require('assert');
7 const { lazy } = require('../index');
8
9 describe('lazy', function () {
10 let o, initializer, called;
11
12 beforeEach(function () {
13 o = {};
14 called = 0;
15 initializer = () => {
16 called += 1;
17 return 'value';
18 };
19 });
20
21 it('does not initialize if not accessed', function () {
22 lazy(o, 'p', initializer);
23 assert.strictEqual(called, 0);
24 assert(Object.keys(o).includes('p'));
25 });
26
27 it('requires a callable initializer', function () {
28 try {
29 lazy(o, 'p', undefined);
30 assert.fail('should reject un-callable initializer');
31 } catch (e) {
32 assert(e instanceof TypeError, `expected 'TypeError', got '${e.name}'`);
33 }
34 });
35
36 it('initializes once when accessed', function () {
37 lazy(o, 'p', initializer);
38 const v = o.p;
39 const v2 = o.p;
40 assert.strictEqual(called, 1);
41 assert.strictEqual(v, 'value');
42 assert.strictEqual(v2, 'value');
43 });
44
45 it('handles symbolic properties', function () {
46 const s = Symbol('s');
47 lazy(o, s, initializer);
48 const v = o[s];
49 assert.strictEqual(called, 1);
50 assert.strictEqual(v, 'value');
51 });
52
53 it('can be overwritten before being read', function () {
54 const expected = 'foobar';
55 lazy(o, 'p', initializer);
56 o.p = expected;
57 const v = o.p;
58 assert.strictEqual(v, expected);
59 assert.strictEqual(called, 0);
60 });
61
62 it('cannot be overwritten before being read if eventually not writable', function () {
63 lazy(o, 'p', initializer, {
64 writable: false,
65 });
66 try {
67 o.p = 'nope';
68 assert.fail('should disallow setting non-writable property');
69 } catch (e) {
70 assert(e instanceof TypeError, `expected 'TypeError', got ${e.name}`);
71 }
72 });
73
74 it('async initializer awkwardly needs await on all gets but technically works', async function () {
75 lazy(o, 'p', async () => {
76 called += 1;
77 return 'value';
78 });
79 const v = await o.p;
80 assert.strictEqual(called, 1);
81 assert.strictEqual(v, 'value');
82 });
83
84 it('prototypical lazy sets value on prototype by default', function () {
85 lazy(o, 'p', initializer);
86 o.id = 'proto';
87 const o1 = Object.create(o);
88 o1.id = 'o1';
89 const o2 = Object.create(o);
90 o2.id = 'o2';
91 const v1 = o1.p;
92 const v2 = o2.p;
93
94 assert.strictEqual(v1, 'value');
95 assert.strictEqual(v2, 'value');
96 assert.strictEqual(called, 1);
97 assert(!Object.hasOwnProperty.call(o1, 'p'));
98 assert(!Object.hasOwnProperty.call(o2, 'p'));
99 });
100
101 it('prototypical lazy sets value on inherited objects when requested', function () {
102 lazy(o, 'p', initializer, undefined, false);
103 o.id = 'proto';
104 const o1 = Object.create(o);
105 o1.id = 'o1';
106 const o2 = Object.create(o);
107 o2.id = 'o2';
108 const v1 = o1.p;
109 const v2 = o2.p;
110
111 assert.strictEqual(v1, 'value');
112 assert.strictEqual(v2, 'value');
113 assert.strictEqual(called, 2);
114 assert(Object.hasOwnProperty.call(o1, 'p'));
115 assert(Object.hasOwnProperty.call(o2, 'p'));
116 });
117
118 }); // lazy