Jsdom: localstorage не работает в jsdom

Созданный на 28 мая 2015  ·  29Комментарии  ·  Источник: jsdom/jsdom

Эй, ребята,

Кто-нибудь работает над реализацией API-интерфейсов localStorage / sessionStorage в jsdom?

С уважением,
Альваро

best-fixed-by-webidl2js feature html living standard

Самый полезный комментарий

Решение @justinmchase отлично подходит для тестирования. Возможно, вы также захотите добавить sessionStorage.

    var jsdom = require('jsdom').jsdom;
    document = jsdom('hello world');
    window = document.defaultView;
    navigator = window.navigator;
    window.localStorage = window.sessionStorage = {
        getItem: function (key) {
            return this[key];
        },
        setItem: function (key, value) {
            this[key] = value;
        }
    };

ФАИК это хорошо издевается. Исправляет некоторые из моих тестов. Спасибо.

Все 29 Комментарий

К сожалению, это довольно сложно без прокси ES2015 :(. Мы могли бы заставить работать getItem / setItem / и т. Д., Но корректное применение изменений свойств на самом деле невозможно.

Было бы неплохо иметь хотя бы опыт работы в памяти, то есть localStorage, имеющий поведение, подобное sessionStorage.
это должно быть проще, не так ли?

Нет, потому что мы не можем достаточно хорошо эмулировать API. Для простых случаев использования (т.е. только с использованием getItem / setItem) это может сработать, но как только к свойствам будет осуществлен прямой доступ, наша реализация выйдет из строя.

@Sebmaster : Я написал простую прокладку для интерфейса хранилища для использования в моих модульных тестах для отдельного пакета.

https://github.com/mnahkies/node-storage-shim

В настоящее время это временное решение, и оно запрещает установку ключей, которые конфликтуют с именами методов интерфейса хранилища. Он также в настоящее время не реализует части спецификации, запускающие событие.

Real localStorage действительно позволяет устанавливать эти ключи с помощью setItem, но использование доступа к свойствам уничтожает определения функций, и после установки вы также не можете получить доступ к этим ключам, используя доступ к свойствам.

Однако, помимо этих ограничений, я думаю, что это довольно точная реализация:
https://github.com/mnahkies/node-storage-shim/blob/master/test.js

Не могли бы вы подробнее рассказать о том, какие аспекты API не могут быть достаточно хорошо эмулированы?

Насколько я могу судить, главное, что в настоящее время невозможно эмулировать, - это запуск события хранилища в ответ на установку свойства.

Я был бы счастлив попытаться интегрировать его с jsdom, если вы считаете, что он достаточно полон с упомянутыми оговорками.

Не могли бы вы подробнее рассказать о том, какие аспекты API не могут быть достаточно хорошо эмулированы?

localStorage поддерживает установку любого имени свойства непосредственно в интерфейсе, как в

localStorage.myKey = "myVal";
localStorage.getItem('myKey') // 'myVal'

localStorage.setItem('otherKey', 'val')
localStorage.otherKey // 'val'

который мы могли бы отчасти подражать, всегда создавая геттер, если вызывается setItem, но мы не сможем поддерживать обратный путь (установка произвольных свойств объекта) без прокси.

Правильно, я предполагаю, что основная проблема, связанная с установкой ключей по доступу к свойствам, заключается в том, что мы не можем правильно преобразовать значения в строки, как это делает настоящий интерфейс.

Да, это очень важный аспект локального хранилища. Для этого нам понадобится ES6 Proxy, v8 еще не реализовал его (у Microsoft и Mozilla уже есть!)

Мы сталкиваемся с одним и тем же препятствием в нескольких местах в jsdom.

Я не понимаю, почему это не сработает:

var localStorage = {
  getItem: function (key) {
    return this[key];
  },
  setItem: function (key, value) {
    this[key] = value;
  }
};

localStorage.setItem('foo', 'bar');
localStorage.bar = 'foo'
assert(localStorage.foo === 'bar')
assert(localStorage.getItem('bar') === 'foo')

Что мне не хватает?

Когда вы устанавливаете элементы, используя доступ к свойствам, реальный localStorage всегда приводит значение к строке.

Например:

localStorage.foo = 35
assert(typeof localStorage.foo === "string")

localStorage.foo = {my: 'object'}
assert(localStorage.foo === "[object Object]")

Это невозможно без прокси ES6 и является важной характеристикой localStorage.

Понятно. Хотя, если ты так стыдишься;)

Я думаю, что более важно, если вы делаете это с объектами, это, вероятно, ошибка, которая будет замаскирована jsdom, а затем возникнет в браузере.

+1

+1

возможно, что-то подобное сработает https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage ?

Я думаю, вы обнаружите, что вам все еще не хватает некоторых характеристик реального локального хранилища, как описано выше.

Связанное решение является хорошим полифиллом, но я не думаю, что это будет достаточно точная реализация для jsdom.

@mnahkies Честно говоря, полифилл лучше, чем ничего (это то, что у вас есть сейчас). Вы должны просто вставить его и добавить некоторую документацию об ограничениях, которые в любом случае никогда не достигнут 99% людей.

+1 к последнему комментарию, пример на MDN использует setItem и getItem качестве средств доступа к хранилищу. Так что это можно рассматривать как распространенный вариант использования, которого нам действительно не хватает.

На данный момент я решаю это с помощью jasmine.createSpy (поскольку я работаю с Jasmine, другие шпионские библиотеки также могут это делать)

Решение @justinmchase отлично подходит для тестирования. Возможно, вы также захотите добавить sessionStorage.

    var jsdom = require('jsdom').jsdom;
    document = jsdom('hello world');
    window = document.defaultView;
    navigator = window.navigator;
    window.localStorage = window.sessionStorage = {
        getItem: function (key) {
            return this[key];
        },
        setItem: function (key, value) {
            this[key] = value;
        }
    };

ФАИК это хорошо издевается. Исправляет некоторые из моих тестов. Спасибо.

Он исправляет ваши тесты до тех пор, пока кто-то случайно не запишет объект в локальное хранилище, используя доступ к свойствам, и не создаст тонкую ошибку.

На мой взгляд, вам было бы лучше иметь объект, который абстрагирует хранилище с заменяемым сервером хранилища, с которым вы можете работать с решением в памяти в своих тестах.

Это также упрощает выполнение более сложных вещей, таких как шифрование, сжатие, запись через кеширование и т. Д., Если вам это потребуется позже.

Пора!!!

Node.js v6 отсутствует, а вместе с ним ... ПРОКСИ.

@Sebmaster , не могли бы вы обновить webidl2js, чтобы иметь возможность генерировать прокси, когда присутствуют именованные геттеры / сеттеры / удалители? Я думаю, мы должны сначала нацелить этот запрос, прежде чем беспокоиться о других вещах, таких как NamedNodeMap, NodeList или что-то еще. Это красиво и автономно и не повлияет на производительность основных примитивов.

https://html.spec.whatwg.org/multipage/webstorage.html#storage -2 имеет IDL, который нам необходимо поддерживать. Я думаю, что можно было бы просто сделать делегирование поведения прокси-сервера getItem имплементации и т. Д.

+1

Вы можете использовать node-localstorage

var LocalStorage = require('node-localstorage').LocalStorage;
global.localStorage = new LocalStorage('./build/localStorageTemp');

global.document = jsdom('');

global.window = document.defaultView;
global.window.localStorage = global.localStorage;

@adjavaherian @justinmchase Я играл с jsdom, который я использую для тестирования как в react-jwt-auth, так и в response-jwt-auth-redux, и он отлично работает. Однако я также работал над enverse - проверками среды на изоморфность развития. Один из чеков - localStorage и sessionStorage . Я столкнулся с той же проблемой, как правильно протестировать, и ваш полифил отлично работает. Спасибо вам, ребята! Было бы здорово, если бы он по умолчанию шел с jsdom.

Теперь для этого предусмотрена поддержка webidl2js. Если люди хотят этим заняться, я бы посоветовал изучить недавно появившуюся реализацию DOMStringList (для набора данных).

На данный момент мы можем хранить все в памяти, хотя в конечном итоге мы должны предоставить способ сохранения на диск, если люди этого захотят. (Люди этого хотят?)

Я бы хотел поработать над этим. Вы можете передать эту проблему мне.

этот макет работает лучше, потому что, когда ключ не сохраняется, localStorage.getItem() должен возвращать null , а не undefined :

const jsdom = require('jsdom');
// setup the simplest document possible
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');;

// get the window object out of the document
const win = doc.defaultView;

win.localStorage = win.sessionStorage = {
  getItem: function(key) {
    const value = this[key];
    return typeof value === 'undefined' ? null : value;
  },
  setItem: function (key, value) {
    this[key] = value;
  },
  removeItem: function(key) {
    return delete this[key]
  }
};

// set globals for mocha that make access to document and window feel
// natural in the test environment
global.document = doc;
global.window = win;

Почему это актуально? Строка ниже отлично работает в среде браузера. json.parse() не работает, если передано undefined качестве аргумента, но отлично работает с параметром null :

let users = JSON.parse(localStorage.getItem('users')) || [];

@simoami Просто win.localStorage = win.sessionStorage = { ... } . Это одна и та же ссылка на объект, назначенная обеим переменным, поэтому вызов любой из функций get / set будет обращаться к одному и тому же базовому объекту. Например, вызов localStorage.set('foo', 'bar') означает, что sessionStorage.get('foo') будет работать - это может быть нормально для простых тестов, но испортит все, что требует отдельного хранилища.

https://gist.github.com/rkurbatov/17468b2ade459a7498c8209800287a03 - мы используем этот полифилл как для локальных / сессионных хранилищ. Он основан на https://github.com/capaj/localstorage-polyfill от @capaj

Те, кто натыкается на эту ветку позже, после недавних улучшений jsdom, вам нужно установить window._localStorage в свое собственное хранилище mocking.

В качестве обратной связи я не смог найти собственные события localStorage, упомянутые как решение этой проблемы.

И как предупреждение всем, кто использует jsdom параллельно и активно использует localStorage, node-localstorage и т. Д. В значительной степени бесполезны, вы можете также заново изобрести колесо, они не предназначены для параллельного использования, а также базовые вещи, такие как for(var key in localStorage) или Object.keys(localStorage) и т. д., тоже не работают

Была ли эта страница полезной?
0 / 5 - 0 рейтинги