Jsdom: localstorage no funciona dentro de jsdom

Creado en 28 may. 2015  ·  29Comentarios  ·  Fuente: jsdom/jsdom

Hey gente,

¿Alguien está trabajando en la implementación de las API localStorage / sessionStorage en jsdom?

Saludos,
Álvaro

best-fixed-by-webidl2js feature html living standard

Comentario más útil

La solución de @justinmchase es excelente para realizar pruebas. Es posible que también desee agregar 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;
        }
    };

FAIK esto se burla bien. Corrige algunas de mis pruebas. Gracias.

Todos 29 comentarios

Desafortunadamente, estos son bastante difíciles sin los proxies ES2015 :(. Podríamos hacer que getItem / setItem / etc. Funcione, pero no es posible aplicar correctamente las modificaciones de propiedad.

Sería bueno tener al menos una experiencia en la memoria, es decir, localStorage con un comportamiento similar a sessionStorage
eso debería ser menos complicado ¿no?

No, porque no podemos emular la API lo suficientemente bien. Para casos de uso simples (es decir, solo usando getItem / setItem) podría funcionar, pero tan pronto como se acceda a las propiedades directamente, nuestra implementación fallará.

@Sebmaster : escribí una corrección simple para la interfaz de almacenamiento para usar en mis pruebas unitarias para un paquete separado.

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

Actualmente es una solución transitoria y prohíbe la configuración de claves que entren en conflicto con los nombres de los métodos de la interfaz de almacenamiento. Tampoco implementa actualmente las partes de activación de eventos de la especificación.

Real localStorage permite la configuración de estas claves usando setItem, pero el uso del acceso a la propiedad elimina las definiciones de las funciones, y una vez configurado, tampoco puede acceder a esas claves mediante el acceso a la propiedad.

Sin embargo, aparte de estas limitaciones, creo que es una implementación bastante fiel:
https://github.com/mnahkies/node-storage-shim/blob/master/test.js

¿Podría explicar qué aspectos de la API no se pueden emular lo suficientemente bien?

Por lo que puedo decir, lo principal que no es posible emular actualmente es el evento de almacenamiento que se activa en respuesta a la configuración de la propiedad.

Me encantaría intentar integrarlo con jsdom si crees que está lo suficientemente completo con las advertencias mencionadas.

¿Podría explicar qué aspectos de la API no se pueden emular lo suficientemente bien?

localStorage admite la configuración de cualquier nombre de propiedad directamente en la interfaz como en

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

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

que podríamos emular de alguna manera creando siempre un getter si se llama a setItem, pero no podremos admitir lo contrario (estableciendo propiedades arbitrarias en el objeto) sin proxies.

Bien, supongo que el problema principal con respecto a la configuración de claves por acceso a la propiedad es que no podemos coaccionar los valores a las cadenas correctamente, como lo hace la interfaz real.

Sí, ese es un aspecto muy importante del almacenamiento local. Necesitamos ES6 Proxy para esto, v8 aún no lo ha implementado (¡microsoft y mozilla ya lo tienen!)

Estamos golpeando este mismo obstáculo en varias ubicaciones en jsdom

No entiendo por qué esto no funcionaría:

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

¿Qué me estoy perdiendo?

Cuando configura elementos mediante el acceso a la propiedad, el localStorage real siempre convierte el valor en una cadena.

P.ej:

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

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

Esto no es posible sin el proxy ES6, y es una característica importante de localStorage

Veo. Aunque si te estás avergonzando;)

Creo que lo más importante es que si estás haciendo eso con objetos, probablemente sea un error que jsdom enmascara y luego ocurre en el navegador.

+1

+1

Creo que encontrará que todavía le falta parte del comportamiento que tiene el almacenamiento local real como se explicó anteriormente.

La solución vinculada es un buen polyfill, pero no creo que sea una implementación lo suficientemente fiel para jsdom

@mnahkies Honestamente, el polyfill es mejor que nada (que es lo que tienes ahora). Debería simplemente ponerlo y agregar algo de documentación sobre las limitaciones que el 99% de las personas nunca alcanzarán de todos modos.

+1 al último comentario, el ejemplo en MDN usa setItem y getItem como accesos al almacenamiento. Por lo tanto, se puede considerar como un caso de uso común que realmente nos falta.

Por ahora, lo resuelvo con jasmine.createSpy (porque trabajo con Jasmine, otras librerías espías también pueden hacerlo)

La solución de @justinmchase es excelente para realizar pruebas. Es posible que también desee agregar 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;
        }
    };

FAIK esto se burla bien. Corrige algunas de mis pruebas. Gracias.

Corrige sus pruebas hasta que alguien escribe accidentalmente un objeto en el almacenamiento local usando el acceso a la propiedad y crea un error sutil.

En mi opinión, sería mejor tener un objeto que abstraiga el almacenamiento con un backend de almacenamiento intercambiable que pueda operar con una solución en memoria en sus pruebas.

Esto también hace que sea más fácil hacer cosas más complejas como cifrado, compresión, escritura a través del almacenamiento en caché, etc. si lo necesita en una fecha posterior.

¡¡¡Es la hora!!!

Node.js v6 está fuera, y con él ... PROXIES.

@Sebmaster , ¿estaría dispuesto a actualizar webidl2js para poder generar proxies cuando estén presentes getters / setters / deleters con nombre? Creo que deberíamos apuntar a esta solicitud primero, antes de preocuparnos por otras cosas como NamedNodeMap o NodeList o lo que sea. Esto es agradable y autónomo y no afectará el rendimiento de las primitivas principales.

https://html.spec.whatwg.org/multipage/webstorage.html#storage -2 tiene el IDL que necesitamos para admitir. Creo que el camino a seguir sería simplemente hacer que el comportamiento de obtención del proxy se delegue al getItem de impl, etc.

+1

Puede usar 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 Estaba jugando con jsdom que uso para probar tanto en react-jwt-auth como en react-jwt-auth-redux y funciona muy bien. Sin embargo, también he estado trabajando en verificaciones de entorno localStorage y sessionStorage . Me he encontrado exactamente con el mismo problema de cómo probarlo correctamente y su polyfil funciona muy bien. ¡Gracias chicos! Sería genial si viene con jsdom por defecto.

El soporte de webidl2js para esto ahora está en su lugar. Si la gente quiere asumir esto, sugeriría estudiar la implementación DOMStringList (para el conjunto de datos) que aterrizó recientemente.

Por ahora podemos mantener todo en la memoria, aunque eventualmente deberíamos proporcionar una forma de almacenarlo en el disco si la gente así lo desea. (¿La gente quiere eso?)

Me gustaría trabajar para implementar esto. Puedes asignarme el problema.

este simulacro funciona mejor porque cuando no se almacena una clave, se supone que localStorage.getItem() devuelve null , no 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 qué es relevante? La siguiente línea funciona bien en un entorno de navegador. json.parse() falla si se pasa undefined como argumento, pero funciona bien con null como parámetro:

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

@simoami Solo ten cuidado con tu asignación encadenada con win.localStorage = win.sessionStorage = { ... } . Es la misma referencia de objeto asignada a ambas variables, por lo que llamar a cualquiera de las funciones get / set accederá al mismo objeto subyacente. Por ejemplo, llamar a localStorage.set('foo', 'bar') significa que sessionStorage.get('foo') funcionará; esto podría estar bien para pruebas simples, pero estropeará cualquier cosa que requiera almacenamiento por separado.

https://gist.github.com/rkurbatov/17468b2ade459a7498c8209800287a03 : utilizamos este polyfill para almacenamiento local / de sesión. Está basado en https://github.com/capaj/localstorage-polyfill de @capaj

Aquellos que se topan con este hilo más adelante, después de las recientes mejoras de jsdom, deben configurar window._localStorage en su propio almacenamiento simulado

Como comentario, no pude encontrar los eventos nativos de localStorage mencionados como una solución a este problema

Y como un aviso para cualquiera que use jsdom en paralelo y use localStorage en gran medida, los node-localstorage , etc.son bastante inútiles, también podría reinventar la rueda, no están diseñados para uso paralelo, también cosas básicas como for(var key in localStorage) o Object.keys(localStorage) etc. tampoco funcionan

¿Fue útil esta página
0 / 5 - 0 calificaciones