Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update LocalStorage error handling #666

Merged
merged 6 commits into from
Nov 15, 2022

Conversation

ryder-wendt
Copy link
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

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

try {
return localStorage.removeItem(key)
} catch (err) {
console.warn(err)
Copy link
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
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.

@@ -871,14 +871,19 @@ describe('store', function () {
})

describe('#get', function () {
it('should not not get an empty record', function () {
expect(store.get('abc') === undefined)
Copy link
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
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
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
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
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
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
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
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
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

'@segment/analytics-next': patch
---

Update LocalStorage expected behavior
Copy link
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
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