Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-25 03:09:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-25 03:09:30 +0300
commit802594d2a7772ed16b844d0a7b91352fa6c573c9 (patch)
tree5974a84c94d533d66123dc3339c5db3050c3586f
parent64e74b20597712c8673cf99b766b7b6210ee6c5e (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/vue_shared/components/local_storage_sync.vue41
-rw-r--r--spec/frontend/helpers/local_storage_helper.js2
-rw-r--r--spec/frontend/helpers/local_storage_helper_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/local_storage_sync_spec.js122
4 files changed, 142 insertions, 27 deletions
diff --git a/app/assets/javascripts/vue_shared/components/local_storage_sync.vue b/app/assets/javascripts/vue_shared/components/local_storage_sync.vue
index b5d6b872547..59155bd4ddc 100644
--- a/app/assets/javascripts/vue_shared/components/local_storage_sync.vue
+++ b/app/assets/javascripts/vue_shared/components/local_storage_sync.vue
@@ -1,4 +1,6 @@
<script>
+import { isEqual } from 'lodash';
+
export default {
props: {
storageKey: {
@@ -6,31 +8,58 @@ export default {
required: true,
},
value: {
- type: String,
+ type: [String, Number, Boolean, Array, Object],
required: false,
default: '',
},
+ asJson: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
watch: {
value(newVal) {
- this.saveValue(newVal);
+ this.saveValue(this.serialize(newVal));
},
},
mounted() {
// On mount, trigger update if we actually have a localStorageValue
- const value = this.getValue();
+ const { exists, value } = this.getStorageValue();
- if (value && this.value !== value) {
+ if (exists && !isEqual(value, this.value)) {
this.$emit('input', value);
}
},
methods: {
- getValue() {
- return localStorage.getItem(this.storageKey);
+ getStorageValue() {
+ const value = localStorage.getItem(this.storageKey);
+
+ if (value === null) {
+ return { exists: false };
+ }
+
+ try {
+ return { exists: true, value: this.deserialize(value) };
+ } catch {
+ // eslint-disable-next-line no-console
+ console.warn(
+ `[gitlab] Failed to deserialize value from localStorage (key=${this.storageKey})`,
+ value,
+ );
+ // default to "don't use localStorage value"
+ return { exists: false };
+ }
},
saveValue(val) {
localStorage.setItem(this.storageKey, val);
},
+ serialize(val) {
+ return this.asJson ? JSON.stringify(val) : val;
+ },
+ deserialize(val) {
+ return this.asJson ? JSON.parse(val) : val;
+ },
},
render() {
return this.$slots.default;
diff --git a/spec/frontend/helpers/local_storage_helper.js b/spec/frontend/helpers/local_storage_helper.js
index cd39b660bfd..0318b80aaef 100644
--- a/spec/frontend/helpers/local_storage_helper.js
+++ b/spec/frontend/helpers/local_storage_helper.js
@@ -35,7 +35,7 @@ export const createLocalStorageSpy = () => {
clear: jest.fn(() => {
storage = {};
}),
- getItem: jest.fn(key => storage[key]),
+ getItem: jest.fn(key => (key in storage ? storage[key] : null)),
setItem: jest.fn((key, value) => {
storage[key] = value;
}),
diff --git a/spec/frontend/helpers/local_storage_helper_spec.js b/spec/frontend/helpers/local_storage_helper_spec.js
index 6b44ea3a4c3..5d9961e7631 100644
--- a/spec/frontend/helpers/local_storage_helper_spec.js
+++ b/spec/frontend/helpers/local_storage_helper_spec.js
@@ -18,11 +18,11 @@ describe('localStorage helper', () => {
localStorage.removeItem('test', 'testing');
- expect(localStorage.getItem('test')).toBeUndefined();
+ expect(localStorage.getItem('test')).toBe(null);
expect(localStorage.getItem('test2')).toBe('testing');
localStorage.clear();
- expect(localStorage.getItem('test2')).toBeUndefined();
+ expect(localStorage.getItem('test2')).toBe(null);
});
});
diff --git a/spec/frontend/vue_shared/components/local_storage_sync_spec.js b/spec/frontend/vue_shared/components/local_storage_sync_spec.js
index 5470171a21e..3ff4c0917f2 100644
--- a/spec/frontend/vue_shared/components/local_storage_sync_spec.js
+++ b/spec/frontend/vue_shared/components/local_storage_sync_spec.js
@@ -12,7 +12,9 @@ describe('Local Storage Sync', () => {
};
afterEach(() => {
- wrapper.destroy();
+ if (wrapper) {
+ wrapper.destroy();
+ }
wrapper = null;
localStorage.clear();
});
@@ -45,23 +47,23 @@ describe('Local Storage Sync', () => {
expect(wrapper.emitted('input')).toBeFalsy();
});
- it('saves updated value to localStorage', () => {
- createComponent({
- props: {
- storageKey,
- value: 'ascending',
- },
- });
-
- const newValue = 'descending';
- wrapper.setProps({
- value: newValue,
- });
-
- return wrapper.vm.$nextTick().then(() => {
- expect(localStorage.getItem(storageKey)).toBe(newValue);
- });
- });
+ it.each('foo', 3, true, ['foo', 'bar'], { foo: 'bar' })(
+ 'saves updated value to localStorage',
+ newValue => {
+ createComponent({
+ props: {
+ storageKey,
+ value: 'initial',
+ },
+ });
+
+ wrapper.setProps({ value: newValue });
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(localStorage.getItem(storageKey)).toBe(String(newValue));
+ });
+ },
+ );
it('does not save default value', () => {
const value = 'ascending';
@@ -125,4 +127,88 @@ describe('Local Storage Sync', () => {
});
});
});
+
+ describe('with "asJson" prop set to "true"', () => {
+ const storageKey = 'testStorageKey';
+
+ describe.each`
+ value | serializedValue
+ ${null} | ${'null'}
+ ${''} | ${'""'}
+ ${true} | ${'true'}
+ ${false} | ${'false'}
+ ${42} | ${'42'}
+ ${'42'} | ${'"42"'}
+ ${'{ foo: '} | ${'"{ foo: "'}
+ ${['test']} | ${'["test"]'}
+ ${{ foo: 'bar' }} | ${'{"foo":"bar"}'}
+ `('given $value', ({ value, serializedValue }) => {
+ describe('is a new value', () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ storageKey,
+ value: 'initial',
+ asJson: true,
+ },
+ });
+
+ wrapper.setProps({ value });
+
+ return wrapper.vm.$nextTick();
+ });
+
+ it('serializes the value correctly to localStorage', () => {
+ expect(localStorage.getItem(storageKey)).toBe(serializedValue);
+ });
+ });
+
+ describe('is already stored', () => {
+ beforeEach(() => {
+ localStorage.setItem(storageKey, serializedValue);
+
+ createComponent({
+ props: {
+ storageKey,
+ value: 'initial',
+ asJson: true,
+ },
+ });
+ });
+
+ it('emits an input event with the deserialized value', () => {
+ expect(wrapper.emitted('input')).toEqual([[value]]);
+ });
+ });
+ });
+
+ describe('with bad JSON in storage', () => {
+ const badJSON = '{ badJSON';
+
+ beforeEach(() => {
+ jest.spyOn(console, 'warn').mockImplementation();
+ localStorage.setItem(storageKey, badJSON);
+
+ createComponent({
+ props: {
+ storageKey,
+ value: 'initial',
+ asJson: true,
+ },
+ });
+ });
+
+ it('should console warn', () => {
+ // eslint-disable-next-line no-console
+ expect(console.warn).toHaveBeenCalledWith(
+ `[gitlab] Failed to deserialize value from localStorage (key=${storageKey})`,
+ badJSON,
+ );
+ });
+
+ it('should not emit an input event', () => {
+ expect(wrapper.emitted('input')).toBeUndefined();
+ });
+ });
+ });
});