@plentymarkets/shop-core 1.3.10 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shop-core",
3
3
  "configKey": "shopCore",
4
- "version": "1.3.10",
4
+ "version": "1.4.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
@@ -3,6 +3,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
3
3
  import { mockNuxtImport } from "@nuxt/test-utils/runtime";
4
4
  import { useCookieBar } from "../useCookieBar.js";
5
5
  import { useCookieConsent } from "../useCookieConsent.js";
6
+ import { ref } from "vue";
6
7
  const { useCookie } = vi.hoisted(() => ({
7
8
  useCookie: vi.fn().mockReturnValue({})
8
9
  }));
@@ -11,11 +12,12 @@ const { useRouter } = vi.hoisted(() => ({
11
12
  go: vi.fn()
12
13
  })
13
14
  }));
14
- mockNuxtImport("useRouter", () => useRouter);
15
15
  mockNuxtImport("useCookie", () => useCookie);
16
+ mockNuxtImport("useRouter", () => useRouter);
16
17
  describe("useCookieBar", () => {
17
18
  beforeEach(() => {
18
19
  const cookieInit = {
20
+ configHash: "",
19
21
  barTitle: "Test",
20
22
  barDescription: "Test",
21
23
  groups: [
@@ -35,19 +37,29 @@ describe("useCookieBar", () => {
35
37
  ]
36
38
  },
37
39
  {
40
+ id: 1,
38
41
  name: "marketing",
42
+ accepted: false,
43
+ showMore: false,
44
+ description: "Marketing cookies description",
39
45
  cookies: [
40
46
  {
41
47
  name: "testCookie",
42
48
  accepted: false,
43
49
  Lifespan: "100",
44
- script: []
50
+ script: [],
51
+ Provider: "TestProvider",
52
+ Status: "Inactive",
53
+ PrivacyPolicy: "/PrivacyPolicy"
45
54
  },
46
55
  {
47
56
  name: "externalScript",
48
57
  accepted: false,
49
58
  Lifespan: "100",
50
- script: ["https://cdn02.plentymarkets.com/mevofvd5omld/frontend/test-cookie-external-script.js"]
59
+ script: ["https://cdn02.plentymarkets.com/mevofvd5omld/frontend/test-cookie-external-script.js"],
60
+ Provider: "TestProvider",
61
+ Status: "Inactive",
62
+ PrivacyPolicy: "/PrivacyPolicy"
51
63
  }
52
64
  ]
53
65
  }
@@ -99,21 +111,41 @@ describe("useCookieBar", () => {
99
111
  expect(document.cookie).not.toContain("testCookie=testValue");
100
112
  });
101
113
  it("should set state read from browser-cookie", () => {
102
- const { initializeCookies, data } = useCookieBar();
103
114
  const browserCookies = {
104
- marketing: {
105
- testCookie: true
115
+ hash: "",
116
+ groups: {
117
+ "CookieBar.essentials.label": {
118
+ "CookieBar.essentials.cookies.payPal.name": true
119
+ },
120
+ marketing: {
121
+ testCookie: true
122
+ }
106
123
  }
107
124
  };
108
- useCookie.mockReturnValue({ value: browserCookies });
125
+ useCookie.mockImplementation((name) => {
126
+ if (name === "consent-cookie") return ref(browserCookies);
127
+ return ref(null);
128
+ });
129
+ const { initializeCookies, data } = useCookieBar();
109
130
  initializeCookies();
110
131
  expect(data.value.groups[1].cookies[0].accepted).toBe(true);
111
132
  });
112
133
  it("should reload the page if a consent cookie is revoked", () => {
113
134
  const { setConsent, initializeCookies, data } = useCookieBar();
114
135
  const RouterGoSpy = vi.fn();
115
- useRouter.mockReturnValue({
116
- go: RouterGoSpy
136
+ useRouter.mockReturnValue({ go: RouterGoSpy });
137
+ useCookie.mockImplementation((name) => {
138
+ if (name === "consent-cookie") {
139
+ return ref({
140
+ hash: "abc123",
141
+ groups: {
142
+ marketing: {
143
+ testCookie: true
144
+ }
145
+ }
146
+ });
147
+ }
148
+ return ref(null);
117
149
  });
118
150
  initializeCookies();
119
151
  data.value.groups[1].cookies[0].accepted = false;
@@ -152,4 +184,27 @@ describe("useCookieBar", () => {
152
184
  expect(data.value.groups[1].cookies[0].accepted).toBe(true);
153
185
  expect(consent.value).toBe(true);
154
186
  });
187
+ it("should show the cookie bar if hash is missing", () => {
188
+ const browserCookies = { groups: {} };
189
+ useCookie.mockImplementation((name) => {
190
+ if (name === "consent-cookie") return ref(browserCookies);
191
+ return ref(null);
192
+ });
193
+ const { initializeCookies, visible } = useCookieBar();
194
+ initializeCookies();
195
+ expect(visible.value).toBe(true);
196
+ });
197
+ it("should show the cookie bar if hash does not match", () => {
198
+ const browserCookies = {
199
+ hash: "mismatchedHash",
200
+ groups: {}
201
+ };
202
+ useCookie.mockImplementation((name) => {
203
+ if (name === "consent-cookie") return ref(browserCookies);
204
+ return ref(null);
205
+ });
206
+ const { initializeCookies, visible } = useCookieBar();
207
+ initializeCookies();
208
+ expect(visible.value).toBe(true);
209
+ });
155
210
  });
@@ -4,6 +4,9 @@ import { useRegisterCookie } from "../useRegisterCookie.js";
4
4
  vi.mock("nuxt/app", () => ({
5
5
  useRuntimeConfig: vi.fn()
6
6
  }));
7
+ vi.mock("../../utils/runtime", () => ({
8
+ isClient: vi.fn(() => false)
9
+ }));
7
10
  describe("useRegisterCookie", () => {
8
11
  it("adds a cookie to the specified group", () => {
9
12
  const { add } = useRegisterCookie();
@@ -1,5 +1,5 @@
1
- import type { Ref } from 'vue';
2
- import type { Cookie, CookieGroupFromNuxtConfig } from '../types/index.js';
1
+ import { type Ref } from 'vue';
2
+ import type { Cookie, CookieGroup, CookieGroupFromNuxtConfig } from '../types/index.js';
3
3
  /**
4
4
  * @description Composable for managing cookie consent bar.
5
5
  * @example
@@ -10,7 +10,8 @@ import type { Cookie, CookieGroupFromNuxtConfig } from '../types/index.js';
10
10
  * ```
11
11
  */
12
12
  export declare const useCookieBar: () => {
13
- changeVisibilityState: () => void;
13
+ cookieGroups: import("vue").ComputedRef<CookieGroup[]>;
14
+ changeVisibilityState: () => boolean;
14
15
  setConsent: () => void;
15
16
  initializeCookies: () => void;
16
17
  setAllCookiesState: (accepted: boolean) => void;
@@ -1,7 +1,8 @@
1
1
  import { useRuntimeConfig, useState, useCookie, useRouter } from "nuxt/app";
2
- import { toRefs } from "vue";
2
+ import { computed, toRefs } from "vue";
3
3
  import { cookieBarHelper } from "../utils/cookieBarHelper.js";
4
4
  import { useCookieConsent } from "./useCookieConsent.js";
5
+ import { sha256 } from "js-sha256";
5
6
  const checkIfScriptIsExternal = (scriptName) => {
6
7
  return scriptName.startsWith("http");
7
8
  };
@@ -12,66 +13,60 @@ export const useCookieBar = () => {
12
13
  visible: false,
13
14
  loading: false
14
15
  }));
15
- const runtimeConfig = useRuntimeConfig();
16
- const initialCookies = runtimeConfig.public.cookieGroups;
17
- const changeVisibilityState = () => {
18
- state.value.visible = !state.value.visible;
19
- };
16
+ const changeVisibilityState = () => state.value.visible = !state.value.visible;
17
+ const cookieGroups = computed(() => state.value.data?.groups || []);
20
18
  const initializeCookies = () => {
21
- const cookies = JSON.parse(JSON.stringify(initialCookies));
22
- const browserCookies = useCookie("consent-cookie");
23
- cookies.groups.forEach((group) => {
19
+ const runtimeCookies = useRuntimeConfig().public.cookieGroups;
20
+ const { configHash, ...configWithoutHash } = runtimeCookies;
21
+ runtimeCookies.configHash = sha256(JSON.stringify(configWithoutHash));
22
+ const browserCookies = useCookie("consent-cookie", { default: () => null });
23
+ const browserHash = browserCookies.value?.hash ?? "";
24
+ runtimeCookies.groups.forEach((group) => {
24
25
  group.cookies.forEach((cookie) => {
25
- const isAccepted = group.name === ESSENTIAL_COOKIE_GROUP || !!browserCookies.value?.[group.name]?.[cookie.name] || false;
26
- if (browserCookies.value?.[group.name]?.[cookie.name] !== void 0 || group.name === ESSENTIAL_COOKIE_GROUP) {
26
+ const isAccepted = group.name === ESSENTIAL_COOKIE_GROUP || (browserCookies.value?.groups?.[group.name]?.[cookie.name] ?? false);
27
+ if (browserCookies.value?.groups?.[group.name]?.[cookie.name] !== void 0 || group.name === ESSENTIAL_COOKIE_GROUP) {
27
28
  cookie.accepted = isAccepted;
28
- if (isAccepted && cookie.script && cookie.script.length) {
29
- fetchScripts(cookie.script);
30
- }
29
+ if (isAccepted && cookie.script && cookie.script.length) fetchScripts(cookie.script);
31
30
  }
32
31
  const { consent } = useCookieConsent(cookie.name);
33
32
  consent.value = isAccepted;
34
33
  });
35
34
  group.accepted = group.cookies.some((cookie) => cookie.accepted);
36
35
  });
37
- state.value.data = cookies;
38
- if (!browserCookies.value) {
39
- state.value.visible = true;
40
- }
36
+ state.value.data = runtimeCookies;
37
+ state.value.visible = browserHash !== runtimeCookies.configHash;
41
38
  };
42
39
  const setConsent = () => {
43
40
  const { getMinimumLifeSpan } = cookieBarHelper();
44
41
  const router = useRouter();
45
- const browserCookies = useCookie("consent-cookie");
42
+ const browserCookies = useCookie("consent-cookie", { default: () => null });
46
43
  let cookieRevoke = false;
47
- const jsonCookie = state.value.data.groups.reduce((accumulator, group) => {
44
+ const jsonCookie = cookieGroups.value.reduce((accumulator, group) => {
48
45
  accumulator[group.name] = group.cookies.reduce((childAccumulator, cookie) => {
49
- const currentStatus = !!browserCookies.value?.[group.name]?.[cookie.name] || false;
46
+ const currentStatus = !!browserCookies.value?.groups?.[group.name]?.[cookie.name] || false;
50
47
  const { consent } = useCookieConsent(cookie.name);
51
48
  childAccumulator[cookie.name] = cookie.accepted || false;
52
49
  consent.value = cookie.accepted || false;
53
50
  if (currentStatus && !consent.value) {
54
51
  cookieRevoke = true;
55
52
  removeCookies(cookie);
56
- } else if (!currentStatus && consent.value && cookie.script && cookie.script.length) {
57
- fetchScripts(cookie.script);
58
53
  }
54
+ if (!currentStatus && consent.value && cookie.script && Array.isArray(cookie.script) && cookie.script.length)
55
+ fetchScripts(cookie.script);
59
56
  return childAccumulator;
60
57
  }, {});
61
58
  return accumulator;
62
59
  }, {});
63
60
  const consentCookie = useCookie("consent-cookie", {
64
61
  path: "/",
65
- maxAge: getMinimumLifeSpan(state.value.data.groups)
62
+ maxAge: getMinimumLifeSpan(cookieGroups.value)
66
63
  });
67
- consentCookie.value = JSON.stringify(jsonCookie);
64
+ consentCookie.value = JSON.stringify({ hash: state.value.data.configHash, groups: jsonCookie });
68
65
  changeVisibilityState();
69
- if (cookieRevoke) {
70
- router.go(0);
71
- }
66
+ if (cookieRevoke) router.go(0);
72
67
  };
73
68
  const setAllCookiesState = (accepted) => {
74
- state.value.data.groups.forEach((group) => {
69
+ cookieGroups.value.forEach((group) => {
75
70
  const isAccepted = group.name === ESSENTIAL_COOKIE_GROUP || accepted;
76
71
  group.accepted = isAccepted;
77
72
  group.cookies.forEach((cookie) => cookie.accepted = isAccepted);
@@ -79,7 +74,7 @@ export const useCookieBar = () => {
79
74
  setConsent();
80
75
  };
81
76
  const accept = (name) => {
82
- state.value.data.groups.forEach((group) => {
77
+ cookieGroups.value.forEach((group) => {
83
78
  group.cookies.forEach((cookie) => {
84
79
  if (cookie.name === name) {
85
80
  cookie.accepted = true;
@@ -104,6 +99,7 @@ export const useCookieBar = () => {
104
99
  };
105
100
  return {
106
101
  ...toRefs(state.value),
102
+ cookieGroups,
107
103
  changeVisibilityState,
108
104
  setConsent,
109
105
  initializeCookies,
@@ -4,5 +4,5 @@ import type { Cookie } from '../types/cookies.js';
4
4
  * @example const { add } = useRegisterCookie();
5
5
  */
6
6
  export declare const useRegisterCookie: () => {
7
- add: (cookie: Cookie, group: string) => void;
7
+ add: (newCookie: Cookie, group: string) => void;
8
8
  };
@@ -1,23 +1,23 @@
1
1
  import { useRuntimeConfig } from "nuxt/app";
2
+ import { isClient } from "../utils/runtime.js";
2
3
  export const useRegisterCookie = () => {
3
- const add = (cookie, group) => {
4
- const runtimeConfig = useRuntimeConfig();
5
- const initialCookies = runtimeConfig.public.cookieGroups;
6
- if (initialCookies && initialCookies.groups) {
7
- initialCookies.groups.forEach((cookieGroup) => {
4
+ const add = (newCookie, group) => {
5
+ if (isClient()) {
6
+ console.error(
7
+ "Cookies can only be registered on the server side, use plugin.server.ts to only run your plugin on the server side"
8
+ );
9
+ return;
10
+ }
11
+ const runtimeCookies = useRuntimeConfig().public.cookieGroups;
12
+ if (runtimeCookies?.groups) {
13
+ for (const cookieGroup of runtimeCookies.groups) {
8
14
  if (cookieGroup.name === group) {
9
- if (!cookieGroup.cookies) {
10
- cookieGroup.cookies = [];
11
- }
12
- if (cookieGroup.cookies.some((existingCookie) => {
13
- return existingCookie.name === cookie.name;
14
- })) {
15
- return;
16
- }
17
- cookieGroup.cookies.push(cookie);
15
+ if (!cookieGroup.cookies) cookieGroup.cookies = [];
16
+ if (cookieGroup.cookies.some((runtimeCookie) => runtimeCookie.name === newCookie.name)) return;
17
+ cookieGroup.cookies.push(newCookie);
18
18
  return;
19
19
  }
20
- });
20
+ }
21
21
  }
22
22
  };
23
23
  return {
@@ -17,6 +17,7 @@ export type CookieGroup = {
17
17
  cookies: Cookie[];
18
18
  };
19
19
  export type CookieGroupFromNuxtConfig = {
20
+ configHash: string;
20
21
  groups: CookieGroup[];
21
22
  barTitle: string;
22
23
  barDescription: string;
@@ -26,3 +27,7 @@ export type JsonCookie = {
26
27
  [key: string]: boolean;
27
28
  };
28
29
  };
30
+ export type BrowserCookies = {
31
+ hash: string;
32
+ groups: JsonCookie;
33
+ } | null;
@@ -0,0 +1 @@
1
+ export declare const isClient: () => boolean | undefined;
@@ -0,0 +1 @@
1
+ export const isClient = () => import.meta.client;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plentymarkets/shop-core",
3
- "version": "1.3.10",
3
+ "version": "1.4.0",
4
4
  "description": "Core module for PlentyONE Shop",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,8 +42,9 @@
42
42
  "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
43
43
  },
44
44
  "dependencies": {
45
- "@plentymarkets/shop-api": "^0.102.1",
45
+ "@plentymarkets/shop-api": "^0.105.0",
46
46
  "@vue-storefront/sdk": "^3.4.1",
47
+ "js-sha256": "^0.11.0",
47
48
  "mitt": "^3.0.1",
48
49
  "resolve": "^1.22.10"
49
50
  },