Jsdom: localstorage não funciona dentro do jsdom

Criado em 28 mai. 2015  ·  29Comentários  ·  Fonte: jsdom/jsdom

Ei pessoal,

Alguém está trabalhando na implementação de APIs localStorage / sessionStorage no jsdom?

Cumprimentos,
Alvaro

best-fixed-by-webidl2js feature html living standard

Comentários muito úteis

A solução de @justinmchase é ótima para teste. Pode ser que você queira adicionar sessionStorage também.

    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;
        }
    };

FAIK isso zomba bem. Corrige alguns dos meus testes. Obrigado.

Todos 29 comentários

Infelizmente, eles são muito difíceis sem os proxies ES2015 :(. Poderíamos fazer getItem / setItem / etc. Funcionar, mas não é possível aplicar as modificações de propriedade corretamente.

Seria bom ter pelo menos uma experiência na memória, ou seja, localStorage com comportamento semelhante a sessionStorage
isso deve ser menos complicado não?

Não, porque não podemos emular a API bem o suficiente. Para casos de uso simples (ou seja, usando apenas getItem / setItem), pode funcionar, mas assim que as propriedades forem acessadas diretamente, nossa implementação será interrompida.

@Sebmaster : escrevi um shim simples para a interface de armazenamento para uso em meus testes de unidade para um pacote separado.

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

Atualmente, é uma solução temporária e proíbe a configuração de chaves que entrem em conflito com os nomes dos métodos da interface de armazenamento. Ele também não implementa atualmente as partes da especificação de disparo de evento.

O real localStorage permite a configuração dessas chaves usando setItem, mas usar o acesso à propriedade acaba com as definições de função e, uma vez definidas, você também não pode acessar essas chaves usando o acesso à propriedade.

No entanto, além dessas limitações, acho que é uma implementação bastante fiel:
https://github.com/mnahkies/node-storage-shim/blob/master/test.js

Você poderia explicar quais aspectos da API não podem ser emulados bem o suficiente?

Pelo que eu posso dizer, a principal coisa que não é possível emular no momento é o disparo do evento de armazenamento em resposta à configuração da propriedade.

Eu ficaria feliz em tentar integrá-lo ao jsdom se você o considerasse completo com as advertências mencionadas.

Você poderia explicar quais aspectos da API não podem ser emulados bem o suficiente?

localStorage suporta a configuração de qualquer nome de propriedade diretamente na interface como em

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

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

que poderíamos emular um pouco criando sempre um getter se setItem for chamado, mas não seremos capazes de oferecer suporte ao contrário (definindo propriedades arbitrárias no objeto) sem proxies.

Certo, acho que o principal problema com relação à configuração de chaves por acesso de propriedade é que não podemos forçar os valores para strings corretamente, como faz a interface real.

Sim, esse é um aspecto muito importante do armazenamento local. Precisamos do ES6 Proxy para isso, a v8 ainda não o implementou (microsoft e mozilla já o tem!)

Estamos atingindo este mesmo bloqueio em vários locais em jsdom

Não entendo por que isso não funcionaria:

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')

o que estou perdendo?

Quando você define itens usando o acesso à propriedade, o localStorage real sempre força o valor para uma string.

Por exemplo:

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

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

Isso não é possível sem o proxy ES6 e é uma característica importante do localStorage

Entendo. Embora se você está fazendo essa vergonha em você;)

Acho mais importante se você estiver fazendo isso com objetos, provavelmente é um bug que seria mascarado por jsdom e, em seguida, ocorreria no navegador

+1

+1

Acho que você descobrirá que ainda está faltando parte do comportamento do armazenamento local real, conforme explicado acima.

A solução vinculada é um bom polyfill, mas não acho que seria uma implementação fiel o suficiente para jsdom

@mnahkies Honestamente, o polyfill é melhor do que nada (que é o que você tem agora). Você deve apenas colocá-lo e adicionar alguma documentação sobre as limitações que 99% das pessoas nunca irão atingir.

+1 no último comentário, exemplo em MDN usa setItem e getItem como acessadores de armazenamento. Portanto, pode ser considerado um caso de uso comum do qual realmente estamos perdendo.

Por enquanto, resolvo com jasmine.createSpy (porque trabalho com Jasmine, outras bibliotecas de espionagem também podem fazer isso)

A solução de @justinmchase é ótima para teste. Pode ser que você queira adicionar sessionStorage também.

    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;
        }
    };

FAIK isso zomba bem. Corrige alguns dos meus testes. Obrigado.

Ele corrige seus testes até que alguém acidentalmente grave um objeto no armazenamento local usando o acesso à propriedade e crie um bug sutil.

Na minha opinião, seria melhor você ter um objeto que abstrai o armazenamento com um back-end de armazenamento trocável que pode operar com uma solução em memória em seus testes.

Isso também torna mais fácil fazer coisas mais complexas como criptografia, compactação, gravação por meio de cache, etc., se você precisar disso em uma data posterior.

Está na hora!!!

Saiu o Node.js v6, e com ele ... PROXIES.

@Sebmaster , você gostaria de atualizar webidl2js para poder gerar proxies quando getters / setters / deleters nomeados estiverem presentes? Acho que devemos direcionar essa solicitação primeiro, antes de nos preocupar com outras coisas como NamedNodeMap ou NodeList ou qualquer outra coisa. Isso é bom e independente e não afetará o desempenho das primitivas do núcleo.

https://html.spec.whatwg.org/multipage/webstorage.html#storage -2 tem o IDL que precisamos oferecer suporte. Acho que o caminho a seguir seria simplesmente delegar o comportamento get do proxy ao getItem do impl, etc.

+1

Você pode usar o 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 Eu estava brincando com jsdom, que uso para testes em react-jwt-auth e react-jwt-auth-redux e funciona muito bem. No entanto, também tenho trabalhado em verificações de ambiente inverso para desenvolvimento isomórfico. Um dos cheques é localStorage e sessionStorage . Corri exatamente para o mesmo problema de como testá-lo corretamente e seu polyfil funciona muito bem. Obrigado pessoal! Seria ótimo se ele viesse com jsdom por padrão.

O suporte da webidl2js para isso já está em vigor. Se as pessoas quiserem fazer isso, sugiro estudar a implementação de DOMStringList (para o conjunto de dados) que pousou recentemente.

Por enquanto, podemos manter tudo na memória, embora, eventualmente, devamos fornecer uma forma de armazenamento em disco, se as pessoas quiserem. (As pessoas querem isso?)

Eu gostaria de trabalhar na implementação disso. Você pode atribuir o problema para mim.

esta simulação funciona melhor porque quando uma chave não está armazenada, localStorage.getItem() deve retornar null , não 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;

Por que é relevante? A linha abaixo funciona bem em um ambiente de navegador. json.parse() falha se passado undefined como argumento, mas funciona bem com null como parâmetro:

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

@simoami Apenas win.localStorage = win.sessionStorage = { ... } . É a mesma referência de objeto atribuída a ambas as variáveis, portanto, a chamada das funções get / set acessará o mesmo objeto subjacente. Por exemplo, chamar localStorage.set('foo', 'bar') significa que sessionStorage.get('foo') funcionará - pode ser adequado para testes simples, mas bagunçará qualquer coisa que requeira armazenamento separado.

https://gist.github.com/rkurbatov/17468b2ade459a7498c8209800287a03 - usamos este polyfill para armazenamentos locais / de sessão. É baseado em https://github.com/capaj/localstorage-polyfill por @capaj

Aqueles que tropeçarem neste tópico mais tarde, após as melhorias recentes do jsdom, você precisa definir window._localStorage para seu próprio armazenamento de simulação

Como feedback, não consegui encontrar os eventos locaisStorage nativos mencionados como uma solução para este problema

E como uma advertência para qualquer um que usa jsdom em paralelo e usa localStorage pesadamente, os node-localstorage etc. são praticamente inúteis, você também pode reinventar a roda, eles não são projetados para uso paralelo, também coisas básicas como for(var key in localStorage) ou Object.keys(localStorage) etc. também não funcionam

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

jacekpl picture jacekpl  ·  4Comentários

machineghost picture machineghost  ·  4Comentários

vsemozhetbyt picture vsemozhetbyt  ·  4Comentários

Progyan1997 picture Progyan1997  ·  3Comentários

mitar picture mitar  ·  4Comentários