Skip to content

Update LocalStorage error handling#666

Merged
ryder-wendt merged 6 commits intomasterfrom
update_LocalStorage_error_handling
Nov 15, 2022
Merged

Update LocalStorage error handling#666
ryder-wendt merged 6 commits intomasterfrom
update_LocalStorage_error_handling

Conversation

@ryder-wendt
Copy link
Copy Markdown
Contributor

@ryder-wendt ryder-wendt commented Nov 9, 2022

This PR is created to fix #631

LocalStorage was reported as becoming unavailable during an IOS private safari session. While unable to reproduce, try/catch have been added enclosing the line where the error was thrown with the intention to catch the error before surfacing on downstream tools.

Testing


[x] Minor unit test added

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Nov 9, 2022

🦋 Changeset detected

Latest commit: 971318b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@segment/analytics-next Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/browser/src/core/user/index.ts Outdated
try {
return localStorage.removeItem(key)
} catch (err) {
console.warn(err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should match the warnings we have for set and get here as well.

Copy link
Copy Markdown
Contributor

@silesky silesky Nov 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ to follow up, we may end up wanting to create a logStorageWarning function so we don't repeat ourselves and avoid duplicate strings -- they take up precious bytes.


describe('#get', function () {
it('should not not get an empty record', function () {
expect(store.get('abc') === undefined)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I wonder how this test was passing before when we should have been returning a value or null.

Copy link
Copy Markdown
Contributor

@silesky silesky Nov 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expect(store.get('abc') === undefined) lacks an actual assertion at the end, so it always passes. It's basically expect(false) or expect(true) haha --there's probably a lint rule for it =)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah of course, that makes sense.

it('should return null values', function () {
store.set('y', { a: null })
expect(store.get('y')).toStrictEqual({ a: null })
})
Copy link
Copy Markdown
Contributor

@silesky silesky Nov 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can add some tests around .get() if the following values are stored:

  • "hello world"
  • ""
  • undefined -- should just pass through "undefined" AFAIK instead of any coercion, so people can easily debug, since it should be an impossible state. Or we could coerce to null. I could go either way.
  • false
  • null

...also a test if localStorage.getItem throws an error.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to cover all the values but having some trouble with making localStorage.getItem unavailable.
I'm only able to throw by messing with Storage.prototype but it has to be placed at bottom of test file, doesn't feel like best practice.

  const store = new LocalStorage()
  beforeEach(function () {
    clear()
  })

  it('should catch without localStorage.getItem', function () {
    store.set('x', 'test')
    Storage.prototype.getItem = null as any
    expect(store.get('x')).toBe(null)
  })
})

Copy link
Copy Markdown
Contributor

@silesky silesky Nov 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use jest spies to mock global methods.

   it('should return null if localStorage throws', () => {
      const getItemSpy = jest
        .spyOn(global.Storage.prototype, 'getItem')
        .mockImplementationOnce(() => {
          throw new Error('getItem fail.')
        })
      store.set('foo', 'some value')
      expect(store.get('foo')).toBeNull()
      expect(getItemSpy).toBeCalledTimes(1)
    })

@ryder-wendt ryder-wendt marked this pull request as ready for review November 14, 2022 16:49
describe('#get', function () {
it('should not not get an empty record', function () {
expect(store.get('abc') === undefined)
it('should throw an error', function () {
Copy link
Copy Markdown
Contributor

@silesky silesky Nov 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"should return null if localStorage throws an error (or does not exist)" would be an improved description

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

@silesky
Copy link
Copy Markdown
Contributor

silesky commented Nov 14, 2022

@ryder-wendt this PR should have a changeset (patch version bump of analytics-next), allowing us to autorelease this library when we choose and giving you credit for it. See: https://github.com/segmentio/analytics-next/blob/master/RELEASING.md#what-is-a-changeset

Comment thread .changeset/itchy-points-flash.md Outdated
'@segment/analytics-next': patch
---

Update LocalStorage expected behavior
Copy link
Copy Markdown
Contributor

@silesky silesky Nov 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A changeset can be as long as you want -- it should usually be a bit nicer than a commit, as it will be used to generate changelog. I would phrase this as "Do not throw errors if localStorage becomes unavailable".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, I was using @1.46.0 as a reference but now looking at all changeset's, they do appear to be more descriptive.

@ryder-wendt ryder-wendt merged commit 5269a3e into master Nov 15, 2022
@ryder-wendt ryder-wendt deleted the update_LocalStorage_error_handling branch November 15, 2022 17:42
@github-actions github-actions bot mentioned this pull request Nov 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Localstorage throws on Private Session Safari iOS

3 participants