Jsdom: localstorage funktioniert nicht innerhalb von jsdom

Erstellt am 28. Mai 2015  ·  29Kommentare  ·  Quelle: jsdom/jsdom

Hallo Leute,

Arbeitet jemand an der Implementierung von localStorage/sessionStorage-APIs in jsdom?

Grüße,
Alvaro

best-fixed-by-webidl2js feature html living standard

Hilfreichster Kommentar

Die Lösung von @justinmchase eignet sich hervorragend zum Testen. Vielleicht möchten Sie auch sessionStorage hinzufügen.

    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 das macht sich gut lustig. Behebt einige meiner Tests. Vielen Dank.

Alle 29 Kommentare

Diese sind ohne ES2015-Proxys leider ziemlich schwierig :(. Wir könnten getItem/setItem/etc. zum Laufen bringen, aber die korrekte Anwendung von Eigenschaftsänderungen ist nicht wirklich möglich.

Es wäre schön, zumindest eine In-Memory-Erfahrung zu haben, dh localStorage hat ein ähnliches Verhalten wie sessionStorage
das sollte weniger kompliziert sein, nein?

Nein, weil wir die API nicht gut genug emulieren können. Für einfache Anwendungsfälle (dh nur die Verwendung von getItem/setItem) mag es funktionieren, aber sobald auf Eigenschaften direkt zugegriffen wird, wird unsere Implementierung zusammenbrechen.

@Sebmaster : Ich habe ein einfaches Shim für die Speicherschnittstelle zur Verwendung in meinen Unit-Tests für ein separates Paket geschrieben.

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

Es ist derzeit eine vorübergehende Lösung und verbietet das Setzen von Schlüsseln, die mit den Methodennamen der Speicherschnittstelle kollidieren. Außerdem werden die ereignisauslösenden Teile der Spezifikation derzeit nicht implementiert.

Real localStorage erlaubt das Setzen dieser Schlüssel mit setItem, aber die Verwendung des Eigenschaftszugriffs bläst die Funktionsdefinitionen weg, und wenn Sie einmal gesetzt sind, können Sie auch nicht über den Eigenschaftszugriff auf diese Schlüssel zugreifen.

Abgesehen von diesen Einschränkungen denke ich jedoch, dass es sich um eine ziemlich originalgetreue Implementierung handelt:
https://github.com/mnahkies/node-storage-shim/blob/master/test.js

Könnten Sie erläutern, welche Aspekte der API nicht gut genug emuliert werden können?

Soweit ich das beurteilen kann, ist die Hauptsache, die derzeit nicht emuliert werden kann, das Auslösen eines Speicherereignisses als Reaktion auf die Eigenschaftseinstellung.

Ich würde mich freuen, es mit jsdom zu integrieren, wenn Sie der Meinung sind, dass es mit den genannten Vorbehalten vollständig genug ist.

Könnten Sie erläutern, welche Aspekte der API nicht gut genug emuliert werden können?

localStorage unterstützt das Setzen beliebiger Eigenschaftsnamen direkt auf der Schnittstelle wie in

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

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

die wir ein wenig emulieren könnten, indem wir immer einen Getter erzeugen, wenn setItem aufgerufen wird, aber wir werden den umgekehrten Weg (das Festlegen beliebiger Eigenschaften des Objekts) ohne Proxys nicht unterstützen können.

Richtig, ich denke, das Hauptproblem beim Festlegen von Schlüsseln durch den Eigenschaftszugriff besteht darin, dass wir die Werte nicht korrekt in Strings umwandeln können, wie dies bei der echten Schnittstelle der Fall ist.

Ja, das ist ein sehr wichtiger Aspekt der lokalen Speicherung. Dafür brauchen wir ES6 Proxy, v8 hat ihn noch nicht implementiert (Microsoft und Mozilla haben ihn schon!)

Wir stoßen an mehreren Orten in jsdom auf dieselbe Straßensperre

Ich verstehe nicht, warum das nicht funktioniert:

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

Was vermisse ich?

Wenn Sie Elemente mithilfe des Eigenschaftszugriffs festlegen, wandelt der echte localStorage den Wert immer in eine Zeichenfolge um.

Z.B:

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

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

Dies ist ohne ES6-Proxy nicht möglich und ein wichtiges Merkmal von localStorage

Ich verstehe. Aber wenn du dich so schämst ;)

Ich denke, wichtiger ist, wenn Sie das mit Objekten machen, ist es wahrscheinlich ein Fehler, der von jsdom maskiert wird und dann im Browser auftritt

+1

+1

Ich denke, Sie werden feststellen, dass Ihnen immer noch ein Teil des Verhaltens des echten lokalen Speichers fehlt, wie oben erläutert.

Die verlinkte Lösung macht ein gutes Polyfill, aber ich glaube nicht, dass es eine treue Implementierung für jsdom wäre

@mnahkies Ehrlich gesagt ist das Polyfill besser als nichts (was Sie jetzt haben). Sie sollten es einfach einfügen und eine Dokumentation zu den Einschränkungen hinzufügen, die 99% der Leute sowieso nie erreichen werden.

+1 zum letzten Kommentar, das Beispiel für MDN verwendet setItem und getItem als Zugriffsrechte auf den Speicher. Es kann also als ein häufiger Anwendungsfall angesehen werden, den wir wirklich vermissen.

Im Moment löse ich es mit jasmine.createSpy (da ich mit Jasmine arbeite, können es auch andere Spionagebibliotheken)

Die Lösung von @justinmchase eignet sich hervorragend zum Testen. Vielleicht möchten Sie auch sessionStorage hinzufügen.

    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 das macht sich gut lustig. Behebt einige meiner Tests. Vielen Dank.

Es behebt Ihre Tests, bis jemand versehentlich ein Objekt mithilfe des Eigenschaftszugriffs in den lokalen Speicher schreibt und einen subtilen Fehler verursacht.

Meiner Meinung nach wäre es besser, ein Objekt zu haben, das den Speicher mit einem austauschbaren Speicher-Backend abstrahiert, das Sie in Ihren Tests mit einer In-Memory-Lösung betreiben können.

Dies erleichtert auch komplexere Dinge wie Verschlüsselung, Komprimierung, Schreiben durch Caching usw., wenn Sie dies zu einem späteren Zeitpunkt benötigen.

Es ist Zeit!!!

Node.js v6 ist draußen und mit ihm... PROXIES.

@Sebmaster , wären Sie

https://html.spec.whatwg.org/multipage/webstorage.html#storage -2 enthält die IDL, die wir unterstützen müssen. Ich denke, der Weg wäre, einfach den Get-Verhaltensdelegierten des Proxys an das getItem des Impls usw.

+1

Sie können node-localstorage verwenden

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 Ich habe mit jsdom herumgespielt, das ich sowohl zum Testen in Reak-jwt-auth als auch in React-jwt-auth-redux verwende und es funktioniert großartig. Ich habe jedoch auch an Enverse- Umgebungschecks für die isomorphe Entwicklung gearbeitet. Einer der Schecks ist localStorage und sessionStorage . Ich bin genau auf das gleiche Problem gelaufen, wie man es richtig testet und dein Polyfil funktioniert großartig. Danke Jungs! Es wäre toll, wenn es standardmäßig mit jsdom kommt.

webidl2js-Unterstützung dafür ist jetzt vorhanden. Wenn die Leute dies übernehmen möchten, würde ich vorschlagen, die DOMStringList-Implementierung (für Dataset) zu studieren, die kürzlich gelandet ist.

Im Moment können wir alles im Speicher behalten, obwohl wir irgendwann eine Möglichkeit bieten sollten, auf Festplatte zu speichern, wenn die Leute dies wünschen. (Wollen die Leute das?)

An der Umsetzung würde ich gerne mitarbeiten. Sie können mir das Problem zuordnen.

Dieser Mock funktioniert besser, denn wenn ein Schlüssel nicht gespeichert ist, soll localStorage.getItem() null , nicht 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;

Warum ist es relevant? Die folgende Zeile funktioniert in einer Browserumgebung einwandfrei. json.parse() schlägt fehl, wenn undefined als Argument übergeben wird, funktioniert aber mit null als Parameter einwandfrei:

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

@simoami Pass auf , dass deine Aufgaben mit win.localStorage = win.sessionStorage = { ... } verkettet sind. Es ist die gleiche Objektreferenz, die beiden Variablen zugewiesen ist, sodass der Aufruf der get/set-Funktionen beider Variablen auf dasselbe zugrunde liegende Objekt zugreift. ZB localStorage.set('foo', 'bar') aufrufen bedeutet, dass sessionStorage.get('foo') funktioniert -- dies mag für einfache Tests in Ordnung sein, wird aber alles durcheinander bringen, was einen separaten Speicher erfordert.

https://gist.github.com/rkurbatov/17468b2ade459a7498c8209800287a03 - wir verwenden diesen Polyfill sowohl für lokale als auch für Sitzungsspeicher. Es basiert auf https://github.com/capaj/localstorage-polyfill von @capaj

Diejenigen, die später über diesen Thread stolpern, müssen nach den jüngsten jsdom-Verbesserungen window._localStorage auf Ihren eigenen Spottspeicher setzen

Als Feedback konnte ich die als Lösung für dieses Problem erwähnten nativen localStorage-Ereignisse nicht finden

Und als Hinweis an jeden, der jsdom parallel verwendet und localStorage stark nutzt, sind die node-localstorage usw. ziemlich nutzlos, Sie können das Rad genauso gut neu erfinden, sie sind auch nicht für die parallele Verwendung ausgelegt grundlegende Dinge wie for(var key in localStorage) oder Object.keys(localStorage) usw. funktionieren auch nicht

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen