Signed-off-by: Mohamed Sayed <k@3zrv.com> PR-URL: https://github.com/nodejs/node/pull/63529 Fixes: https://github.com/nodejs/node/issues/63514 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
148 lines
4.8 KiB
JavaScript
148 lines
4.8 KiB
JavaScript
'use strict';
|
|
|
|
const { skipIfSQLiteMissing, spawnPromisified } = require('../common');
|
|
skipIfSQLiteMissing();
|
|
const tmpdir = require('../common/tmpdir');
|
|
const assert = require('node:assert');
|
|
const { join } = require('node:path');
|
|
const { readdir } = require('node:fs/promises');
|
|
const { test, describe } = require('node:test');
|
|
let cnt = 0;
|
|
|
|
tmpdir.refresh();
|
|
|
|
function nextLocalStorage() {
|
|
return join(tmpdir.path, `${++cnt}.localstorage`);
|
|
}
|
|
|
|
test('Storage instances cannot be created in userland', async () => {
|
|
const cp = await spawnPromisified(process.execPath, [
|
|
'-e', 'new globalThis.Storage()',
|
|
]);
|
|
|
|
assert.strictEqual(cp.code, 1);
|
|
assert.strictEqual(cp.signal, null);
|
|
assert.strictEqual(cp.stdout, '');
|
|
assert.match(cp.stderr, /Error: Illegal constructor/);
|
|
});
|
|
|
|
test('calling "length" getter on invalid this throws', async () => {
|
|
assert.throws(() => Storage.prototype.length, TypeError);
|
|
const { get } = Object.getOwnPropertyDescriptor(Storage.prototype, 'length');
|
|
for (const thisArg of [null, undefined, 1n, -0, NaN, true, false, '', [], {}, Symbol()]) {
|
|
assert.throws(() => get.call(thisArg), TypeError);
|
|
}
|
|
});
|
|
|
|
test('sessionStorage is not persisted', async () => {
|
|
let cp = await spawnPromisified(process.execPath, [
|
|
'-pe', 'sessionStorage.foo = "barbaz"',
|
|
]);
|
|
assert.strictEqual(cp.code, 0);
|
|
assert.match(cp.stdout, /barbaz/);
|
|
|
|
cp = await spawnPromisified(process.execPath, [
|
|
'-pe', 'sessionStorage.foo',
|
|
]);
|
|
assert.strictEqual(cp.code, 0);
|
|
assert.match(cp.stdout, /undefined/);
|
|
assert.strictEqual((await readdir(tmpdir.path)).length, 0);
|
|
});
|
|
|
|
test('localStorage returns undefined and warns without --localstorage-file', async () => {
|
|
const cp = await spawnPromisified(process.execPath, [
|
|
'-pe', 'localStorage',
|
|
]);
|
|
assert.strictEqual(cp.code, 0);
|
|
assert.strictEqual(cp.signal, null);
|
|
assert.match(cp.stdout, /undefined/);
|
|
assert.match(cp.stderr, /ExperimentalWarning:.*localStorage is not available/);
|
|
});
|
|
|
|
test('localStorage is not enumerable without --localstorage-file', async () => {
|
|
const cp = await spawnPromisified(process.execPath, [
|
|
'-pe', 'Object.keys(globalThis).includes("localStorage")',
|
|
]);
|
|
assert.strictEqual(cp.code, 0);
|
|
assert.match(cp.stdout, /false/);
|
|
});
|
|
|
|
test('localStorage is not persisted if it is unused', async () => {
|
|
const cp = await spawnPromisified(process.execPath, [
|
|
'--localstorage-file', nextLocalStorage(),
|
|
'-pe', 'localStorage === globalThis.localStorage',
|
|
]);
|
|
assert.strictEqual(cp.code, 0);
|
|
assert.match(cp.stdout, /true/);
|
|
assert.strictEqual((await readdir(tmpdir.path)).length, 0);
|
|
});
|
|
|
|
test('localStorage is persisted if it is used', async () => {
|
|
const localStorageFile = nextLocalStorage();
|
|
let cp = await spawnPromisified(process.execPath, [
|
|
'--localstorage-file', localStorageFile,
|
|
'-pe', 'localStorage.foo = "barbaz"',
|
|
]);
|
|
assert.strictEqual(cp.code, 0);
|
|
assert.match(cp.stdout, /barbaz/);
|
|
const entries = await readdir(tmpdir.path);
|
|
assert.strictEqual(entries.length, 1);
|
|
assert.match(entries[0], /\d+\.localstorage/);
|
|
|
|
cp = await spawnPromisified(process.execPath, [
|
|
'--localstorage-file', localStorageFile,
|
|
'-pe', 'localStorage.foo',
|
|
]);
|
|
assert.strictEqual(cp.code, 0);
|
|
assert.match(cp.stdout, /barbaz/);
|
|
});
|
|
|
|
|
|
describe('webstorage quota for localStorage and sessionStorage', () => {
|
|
const MAX_STORAGE_SIZE = 10 * 1024 * 1024;
|
|
|
|
for (const storage of ['localStorage', 'sessionStorage']) {
|
|
test(`${storage} can store a max of 10 MB quota`, async () => {
|
|
const args = [];
|
|
if (storage === 'localStorage') {
|
|
args.push('--localstorage-file', nextLocalStorage());
|
|
}
|
|
// Each character is 2 bytes
|
|
args.push('-e', `
|
|
const assert = require('assert');
|
|
${storage}['a'.repeat(${MAX_STORAGE_SIZE} / 2)] = '';
|
|
assert.throws(
|
|
() => { ${storage}.anything = 'should fail'; },
|
|
(err) => {
|
|
assert.strictEqual(err.name, 'QuotaExceededError');
|
|
assert.strictEqual(err.code, 22);
|
|
assert(err instanceof DOMException);
|
|
assert(err instanceof QuotaExceededError);
|
|
assert.strictEqual(err.quota, null);
|
|
assert.strictEqual(err.requested, null);
|
|
return true;
|
|
},
|
|
);
|
|
`);
|
|
const cp = await spawnPromisified(process.execPath, args);
|
|
assert.strictEqual(cp.code, 0);
|
|
});
|
|
}
|
|
});
|
|
|
|
test('disabled with --no-webstorage', async () => {
|
|
for (const api of ['Storage', 'localStorage', 'sessionStorage']) {
|
|
const cp = await spawnPromisified(process.execPath, [
|
|
'--no-webstorage',
|
|
'--localstorage-file',
|
|
'./test/fixtures/localstoragefile-global-test',
|
|
'-e',
|
|
api,
|
|
]);
|
|
|
|
assert.strictEqual(cp.code, 1);
|
|
assert.strictEqual(cp.signal, null);
|
|
assert.strictEqual(cp.stdout, '');
|
|
assert(cp.stderr.includes(`ReferenceError: ${api} is not defined`));
|
|
}
|
|
});
|