dumbmatter / fakeIndexedDB

A pure JS in-memory implementation of the IndexedDB API
Apache License 2.0
562 stars 69 forks source link

A value which is actually Date, but instanceOf Date equals false when use with jest+jsdom. #98

Closed sjnho closed 3 months ago

sjnho commented 6 months ago

I have a objectStore with index via a Date value.

const request = indexedDB.open('testDB',1);
request.onupgradeneeded=()=>{
   const db = request.result;
   const store = db.createObjectStore('testStore');
   store.createIndex('dateIndex','created',{unique:false})
}

Add some test data like {data:"test",created:new Date()} in jest test suite. But when I run as follow code.

const store = db.transaction(['testStore'], 'readwrite').objectStore('testStore');
const index = store.index('dateIndex');
const request = index.openCursor();
request.onsuccess = function () {
    const cursor = request.result;
    if (cursor) {
        cursor.delete();
        cursor.continue();
    } else {
        res();
    }
};

The cursor is always null. And I console.log (index._rawIndex.records).It is an empty array. After my investigation , the reason is jsdom override Date, so the date instance check in valueToKey.ts will return false, step into else and throw new DataError() https://github.com/dumbmatter/fakeIndexedDB/blob/a1c148880d227fa218473efeac11d4e36dfa8b73/src/lib/valueToKey.ts#L11

My workaround is override global.Date

import JSDOMEnvironment from 'jest-environment-jsdom';

// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
  constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
    super(...args);
    this.global.structuredClone = structuredClone;
    this.global.Date = Date;
  }
}

How do you think about it ? Do we need fix it using like Object.prototype.toString.call(input ) === '[object Date]'

Or keep the workaround.

dumbmatter commented 6 months ago

I would think that if jsdom is overwriting the Date object, then the code inside fake-indexeddb should also have that Date object and it should work. But I guess that's not working right.

Can you give me a minimal reproduction to help make debugging easier?

sjnho commented 5 months ago

@dumbmatter Sorry for my slow reply. I create a repo. https://github.com/sjnho/fakeindexeddb-jest-debug

dumbmatter commented 5 months ago

I think Object.prototype.toString.call(input) === '[object Date]' is probably a valid solution... https://stackoverflow.com/a/643827/786644 points out that there are other valid cases where instanceof can fail. In theory someone could make some other object that returns "[object Date]" from toString, but that is probably less likely than using jsdom like you did.

If you want to send a PR feel free! If not just say so and I'll do it, it's only one line of code (and probably a comment explaining it and linking here) - I just want to give you the opportunity to take credit for it with a PR if you want :)

sjnho commented 5 months ago

No problem, I will send a PR. ❤️

dumbmatter commented 3 months ago

This is in v6.0.0 :)