Jsdom: localstorage ne fonctionne pas dans jsdom

Créé le 28 mai 2015  ·  29Commentaires  ·  Source: jsdom/jsdom

Salut les gens,

Quelqu'un travaille-t-il sur la mise en œuvre des API localStorage/sessionStorage dans jsdom ?

Salutations,
Allvaro

best-fixed-by-webidl2js feature html living standard

Commentaire le plus utile

La solution de @justinmchase est idéale pour les tests. Vous voudrez peut-être aussi ajouter 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 ça se moque bien. Corrige certains de mes tests. Merci.

Tous les 29 commentaires

Ceux-ci sont malheureusement assez difficiles sans proxy ES2015 :(. Nous pourrions faire fonctionner getItem/setItem/etc., mais il n'est pas vraiment possible d'appliquer correctement les modifications de propriétés.

Ce serait bien d'avoir au moins une expérience en mémoire, c'est-à-dire localStorage ayant un comportement similaire à sessionStorage
ça devrait être moins compliqué non ?

Non, car nous ne pouvons pas assez émuler l'API. Pour des cas d'utilisation simples (c'est-à-dire en utilisant uniquement getItem/setItem), cela peut fonctionner, mais dès que les propriétés sont accessibles directement, notre implémentation s'effondrera.

@Sebmaster : j'ai écrit une simple cale pour l'interface de stockage à utiliser dans mes tests unitaires pour un package séparé.

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

Il s'agit actuellement d'une solution transitoire, et interdit la définition de clés qui entrent en conflit avec les noms de méthode de l'interface de stockage. Il n'implémente pas non plus actuellement les parties de déclenchement d'événement de la spécification.

Real localStorage permet la définition de ces clés à l'aide de setItem, mais l'utilisation de l'accès aux propriétés supprime les définitions de fonction et, une fois définies, vous ne pouvez pas non plus accéder à ces clés à l'aide de l'accès aux propriétés.

Cependant, à part ces limitations, je pense que c'est une implémentation assez fidèle :
https://github.com/mnahkies/node-storage-shim/blob/master/test.js

Pourriez-vous préciser quels aspects de l'API ne peuvent pas être suffisamment émulés ?

Pour autant que je sache, la principale chose qu'il n'est actuellement pas possible d'émuler est l'événement de stockage déclenché en réponse au paramètre de propriété.

Je serais heureux d'essayer de l'intégrer à jsdom si vous pensiez qu'il était suffisamment complet avec les mises en garde mentionnées.

Pourriez-vous préciser quels aspects de l'API ne peuvent pas être suffisamment émulés ?

localStorage prend en charge la définition de n'importe quel nom de propriété directement sur l'interface comme dans

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

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

que nous pourrions émuler quelque peu en créant toujours un getter si setItem est appelé, mais nous ne pourrons pas prendre en charge l'inverse (définir des propriétés arbitraires sur l'objet) sans proxy.

Bon, je suppose que le principal problème en ce qui concerne la définition des clés par accès à la propriété est que nous ne pouvons pas forcer correctement les valeurs en chaînes, comme le fait la véritable interface.

Oui, c'est un aspect très important du stockage local. Nous avons besoin du proxy ES6 pour cela, la v8 ne l'a pas encore implémenté (microsoft et mozilla l'ont déjà !)

Nous heurtons ce même barrage routier à plusieurs endroits dans jsdom

Je ne comprends pas pourquoi cela ne fonctionnerait pas :

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'est-ce que je rate?

Lorsque vous définissez des éléments à l'aide de l'accès aux propriétés, le vrai localStorage contraint toujours la valeur à une chaîne.

Par exemple:

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

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

Ceci n'est pas possible sans proxy ES6, et c'est une caractéristique importante de localStorage

Je vois. Mais si tu te fais ça honte ;)

Je pense que plus important encore, si vous faites cela avec des objets, il s'agit probablement d'un bogue qui serait masqué par jsdom et se produirait ensuite dans le navigateur.

+1

+1

peut-être que quelque chose comme ça fonctionnerait https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage ?

Je pense que vous constaterez qu'il vous manque encore une partie du comportement du véritable stockage local, comme expliqué ci-dessus.

La solution liée fait un bon polyfill mais je ne pense pas que ce serait une implémentation assez fidèle pour jsdom

@mnahkies Honnêtement, le polyfill est mieux que rien (c'est ce que vous avez maintenant). Vous devriez simplement le mettre et ajouter de la documentation sur les limitations que 99% des gens ne rencontreront jamais de toute façon.

+1 au dernier commentaire, l' exemple sur MDN utilise setItem et getItem comme accesseurs au stockage. Il peut donc être considéré comme un cas d'utilisation courant qui nous manque vraiment.

Pour l'instant, je le résous avec jasmine.createSpy (parce que je travaille avec Jasmine, d'autres librairies d'espionnage peuvent aussi le faire)

La solution de @justinmchase est idéale pour les tests. Vous voudrez peut-être aussi ajouter 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 ça se moque bien. Corrige certains de mes tests. Merci.

Il corrige vos tests jusqu'à ce que quelqu'un écrive accidentellement un objet sur le stockage local en utilisant l'accès aux propriétés et crée un bogue subtil.

À mon avis, vous feriez mieux d'avoir un objet qui résume le stockage avec un backend de stockage échangeable que vous pouvez utiliser avec une solution en mémoire dans vos tests.

Cela facilite également les tâches plus complexes telles que le cryptage, la compression, l'écriture via la mise en cache, etc. si vous en avez besoin ultérieurement.

C'est l'heure!!!

Node.js v6 est sorti, et avec lui... PROXIES.

@Sebmaster , seriez-vous prêt à mettre à jour webidl2js pour pouvoir générer des proxys lorsque des getters/setters/deleters nommés sont présents ? Je pense que nous devrions cibler cette demande en premier, avant de nous inquiéter d'autres choses comme NamedNodeMap ou NodeList ou autre. C'est agréable et autonome et n'affectera pas les performances des primitives de base.

https://html.spec.whatwg.org/multipage/webstorage.html#storage -2 a l'IDL que nous devons prendre en charge. Je pense que la voie à suivre serait de simplement faire en sorte que le comportement get du proxy délègue au getItem de l'impl, etc.

+1

Vous pouvez utiliser 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 Je jouais avec jsdom que j'utilise pour tester à la fois dans react-jwt-auth et react-jwt-auth-redux et cela fonctionne très bien. Cependant, j'ai également travaillé sur enverse - vérifications environnementales pour le développement isomorphe. L'un des chèques est localStorage et sessionStorage . J'ai rencontré exactement le même problème de comment le tester correctement et votre polyfil fonctionne très bien. Merci les gars! Ce serait génial s'il est livré avec jsdom par défaut.

La prise en charge de webidl2js pour cela est maintenant en place. Si les gens veulent s'en charger, je suggérerais d'étudier l'implémentation DOMStringList (pour l'ensemble de données) qui a récemment atterri.

Pour l'instant, nous pouvons tout garder en mémoire, même si nous devrions éventuellement fournir un moyen de stocker sur disque si les gens le souhaitent. (Est-ce que les gens veulent ça ?)

J'aimerais travailler à sa mise en œuvre. Vous pouvez m'attribuer le problème.

cette simulation fonctionne mieux car lorsqu'une clé n'est pas stockée, localStorage.getItem() est censé renvoyer null , pas 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;

Pourquoi est-ce pertinent ? La ligne ci-dessous fonctionne bien dans un environnement de navigateur. json.parse() échoue si undefined est passé comme argument, mais fonctionne bien avec null comme paramètre :

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

@simoami Faites juste attention à votre enchaînement de devoirs avec win.localStorage = win.sessionStorage = { ... } . C'est la même référence d'objet affectée aux deux variables, donc l'appel des fonctions get/set de l'une ou l'autre accédera au même objet sous-jacent. Par exemple, appeler localStorage.set('foo', 'bar') signifie que sessionStorage.get('foo') fonctionnera -- cela peut convenir pour des tests simples, mais gâchera tout ce qui nécessite un stockage séparé.

https://gist.github.com/rkurbatov/17468b2ade459a7498c8209800287a03 - nous utilisons ce polyfill pour les deux stockages locaux/de session. Il est basé sur https://github.com/capaj/localstorage-polyfill par @capaj

Ceux qui tombent sur ce fil plus tard, après les récentes améliorations de jsdom, vous devez définir window._localStorage sur votre propre stockage moqueur

En guise de retour, je n'ai pas trouvé les événements natifs localStorage mentionnés comme solution à ce problème

Et en guise d'avertissement à tous ceux qui utilisent jsdom en parallèle et utilisent fortement localStorage, les node-localstorage etc. sont pratiquement inutiles, vous pourriez aussi bien réinventer la roue, ils ne sont pas conçus pour une utilisation parallèle, aussi les choses de base comme for(var key in localStorage) ou Object.keys(localStorage) etc. ne fonctionnent pas non plus

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

lehni picture lehni  ·  4Commentaires

domenic picture domenic  ·  3Commentaires

cg433n picture cg433n  ·  3Commentaires

kilianc picture kilianc  ·  4Commentaires

khalyomede picture khalyomede  ·  3Commentaires