/* eslint-env mocha */
+/* eslint-disable capitalized-comments */
+/* eslint-disable security/detect-object-injection */
'use strict';
const assert = require('assert');
beforeEach(function () {
o = {};
- called = false;
+ called = 0;
initializer = () => {
- called = true;
+ called += 1;
return 'value';
};
});
it('does not initialize if not accessed', function () {
lazy(o, 'p', initializer);
- assert.strictEqual(called, false);
+ assert.strictEqual(called, 0);
assert(Object.keys(o).includes('p'));
});
- it('wants a callable initializer', function () {
+ it('requires a callable initializer', function () {
try {
lazy(o, 'p', undefined);
assert.fail('should reject un-callable initializer');
}
});
- it('initializes if accessed', function () {
+ it('initializes once when accessed', function () {
lazy(o, 'p', initializer);
const v = o.p;
- assert.strictEqual(called, true);
+ const v2 = o.p;
+ assert.strictEqual(called, 1);
assert.strictEqual(v, 'value');
+ assert.strictEqual(v2, 'value');
});
it('handles symbolic properties', function () {
const s = Symbol('s');
lazy(o, s, initializer);
const v = o[s];
- assert.strictEqual(called, true);
+ assert.strictEqual(called, 1);
assert.strictEqual(v, 'value');
});
o.p = expected;
const v = o.p;
assert.strictEqual(v, expected);
- assert.strictEqual(called, false);
+ assert.strictEqual(called, 0);
});
-}); // lazy
+ it('cannot be overwritten before being read if eventually not writable', function () {
+ lazy(o, 'p', initializer, {
+ writable: false,
+ });
+ try {
+ o.p = 'nope';
+ assert.fail('should disallow setting non-writable property');
+ } catch (e) {
+ assert(e instanceof TypeError, `expected 'TypeError', got ${e.name}`);
+ }
+ });
+
+ it('async initializer awkwardly needs await on all gets but technically works', async function () {
+ lazy(o, 'p', async () => {
+ called += 1;
+ return 'value';
+ });
+ const v = await o.p;
+ assert.strictEqual(called, 1);
+ assert.strictEqual(v, 'value');
+ });
+
+ it('prototypical lazy sets value on prototype by default', function () {
+ lazy(o, 'p', initializer);
+ o.id = 'proto';
+ const o1 = Object.create(o);
+ o1.id = 'o1';
+ const o2 = Object.create(o);
+ o2.id = 'o2';
+ const v1 = o1.p;
+ const v2 = o2.p;
+
+ assert.strictEqual(v1, 'value');
+ assert.strictEqual(v2, 'value');
+ assert.strictEqual(called, 1);
+ assert(!Object.hasOwnProperty.call(o1, 'p'));
+ assert(!Object.hasOwnProperty.call(o2, 'p'));
+ });
+
+ it('prototypical lazy sets value on inherited objects when requested', function () {
+ lazy(o, 'p', initializer, undefined, false);
+ o.id = 'proto';
+ const o1 = Object.create(o);
+ o1.id = 'o1';
+ const o2 = Object.create(o);
+ o2.id = 'o2';
+ const v1 = o1.p;
+ const v2 = o2.p;
+
+ assert.strictEqual(v1, 'value');
+ assert.strictEqual(v2, 'value');
+ assert.strictEqual(called, 2);
+ assert(Object.hasOwnProperty.call(o1, 'p'));
+ assert(Object.hasOwnProperty.call(o2, 'p'));
+ });
+
+}); // lazy
\ No newline at end of file