Skip to content

Commit 64d0c21

Browse files
committed
Improve test coverage
1 parent 69be5b0 commit 64d0c21

File tree

2 files changed

+329
-50
lines changed

2 files changed

+329
-50
lines changed

acrobat/scripts/reactiveStore.js

Lines changed: 0 additions & 50 deletions
This file was deleted.

test/scripts/threeInOne.jest.js

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
/* eslint-env jest */
6+
7+
import threeInOne from '../../acrobat/scripts/threeInOne.js';
8+
9+
// Mock the commerceOrigin for testing
10+
const mockCommerceOrigin = 'https://commerce.adobe.com';
11+
12+
describe('threeInOne', () => {
13+
let mockElement;
14+
let mockParent;
15+
16+
beforeEach(() => {
17+
// Clear the DOM
18+
document.body.innerHTML = '';
19+
20+
// Reset any intervals
21+
jest.clearAllTimers();
22+
jest.useFakeTimers();
23+
24+
// Create mock elements
25+
mockParent = document.createElement('div');
26+
mockElement = document.createElement('a');
27+
mockParent.appendChild(mockElement);
28+
document.body.appendChild(mockParent);
29+
});
30+
31+
afterEach(() => {
32+
jest.clearAllTimers();
33+
jest.useRealTimers();
34+
});
35+
36+
describe('mas:resolved event listener', () => {
37+
it('should add threeInOneReady class when target is a checkout link', () => {
38+
const mockTarget = {
39+
isCheckoutLink: true,
40+
classList: { add: jest.fn() },
41+
};
42+
43+
const event = new CustomEvent('mas:resolved');
44+
Object.defineProperty(event, 'target', {
45+
value: mockTarget,
46+
writable: false,
47+
});
48+
49+
// Dispatch the event
50+
document.dispatchEvent(event);
51+
52+
// Fast forward timers to trigger the interval
53+
jest.advanceTimersByTime(100);
54+
55+
expect(mockTarget.classList.add).toHaveBeenCalledWith('threeInOneReady');
56+
});
57+
58+
it('should not add class immediately if target is not a checkout link', () => {
59+
const mockTarget = {
60+
isCheckoutLink: false,
61+
classList: { add: jest.fn() },
62+
};
63+
64+
const event = new CustomEvent('mas:resolved');
65+
Object.defineProperty(event, 'target', {
66+
value: mockTarget,
67+
writable: false,
68+
});
69+
70+
document.dispatchEvent(event);
71+
jest.advanceTimersByTime(100);
72+
73+
expect(mockTarget.classList.add).not.toHaveBeenCalled();
74+
});
75+
76+
it('should eventually add class when target becomes a checkout link', () => {
77+
const mockTarget = {
78+
isCheckoutLink: false,
79+
classList: { add: jest.fn() },
80+
};
81+
82+
const event = new CustomEvent('mas:resolved');
83+
Object.defineProperty(event, 'target', {
84+
value: mockTarget,
85+
writable: false,
86+
});
87+
88+
document.dispatchEvent(event);
89+
90+
// Initially not a checkout link
91+
jest.advanceTimersByTime(100);
92+
expect(mockTarget.classList.add).not.toHaveBeenCalled();
93+
94+
// Now becomes a checkout link
95+
mockTarget.isCheckoutLink = true;
96+
jest.advanceTimersByTime(100);
97+
98+
expect(mockTarget.classList.add).toHaveBeenCalledWith('threeInOneReady');
99+
});
100+
});
101+
102+
describe('threeInOne function', () => {
103+
it('should process elements with data-wcs-osi and data-modal="crm"', async () => {
104+
// Setup element with required attributes
105+
mockElement.setAttribute('data-wcs-osi', 'vQmS1H18A6_kPd0tYBgKnp-TQIF0GbT6p8SH8rWcLMs');
106+
mockElement.setAttribute('data-modal', 'crm');
107+
mockElement.setAttribute('data-modal-id', 'some-id');
108+
mockElement.href = 'original-href';
109+
110+
await threeInOne();
111+
112+
const processedElement = mockParent.querySelector('a');
113+
114+
// Check attributes were modified correctly
115+
expect(processedElement.getAttribute('data-modal')).toBeNull();
116+
expect(processedElement.getAttribute('data-modal-id')).toBeNull();
117+
expect(processedElement.getAttribute('data-checkout-workflow-step')).toBe('email');
118+
119+
// Check href was updated
120+
const expectedHref = `${mockCommerceOrigin}/store/commitment?items%5B0%5D%5Bid%5D=7C30A05FE0EC0BA92566737E720C4692&cli=doc_cloud&ctx=fp&co=US&lang=en`;
121+
expect(processedElement.href).toBe(expectedHref);
122+
});
123+
124+
it('should not process elements without data-modal="crm"', async () => {
125+
mockElement.setAttribute('data-wcs-osi', 'test-offer-1');
126+
mockElement.setAttribute('data-modal', 'other');
127+
mockElement.setAttribute('data-modal-id', 'some-id');
128+
const originalHref = 'original-href';
129+
mockElement.href = originalHref;
130+
131+
await threeInOne();
132+
133+
const element = mockParent.querySelector('a');
134+
135+
// Attributes should remain unchanged
136+
expect(element.getAttribute('data-modal')).toBe('other');
137+
expect(element.getAttribute('data-modal-id')).toBe('some-id');
138+
expect(element.getAttribute('data-checkout-workflow-step')).toBeNull();
139+
expect(element.href).toContain(originalHref);
140+
});
141+
142+
it('should not process elements without data-wcs-osi', async () => {
143+
mockElement.setAttribute('data-modal', 'crm');
144+
mockElement.setAttribute('data-modal-id', 'some-id');
145+
const originalHref = 'original-href';
146+
mockElement.href = originalHref;
147+
148+
await threeInOne();
149+
150+
const element = mockParent.querySelector('a');
151+
152+
// Attributes should remain unchanged
153+
expect(element.getAttribute('data-modal')).toBe('crm');
154+
expect(element.getAttribute('data-modal-id')).toBe('some-id');
155+
expect(element.getAttribute('data-checkout-workflow-step')).toBeNull();
156+
expect(element.href).toContain(originalHref);
157+
});
158+
159+
it('should handle elements with unknown offer IDs', async () => {
160+
mockElement.setAttribute('data-wcs-osi', 'unknown-offer-id');
161+
mockElement.setAttribute('data-modal', 'crm');
162+
mockElement.setAttribute('data-modal-id', 'some-id');
163+
const originalHref = 'original-href';
164+
mockElement.href = originalHref;
165+
166+
await threeInOne();
167+
168+
const element = mockParent.querySelector('a');
169+
170+
// Attributes should be modified but href should remain unchanged
171+
expect(element.getAttribute('data-modal')).toBeNull();
172+
expect(element.getAttribute('data-modal-id')).toBeNull();
173+
expect(element.getAttribute('data-checkout-workflow-step')).toBe('email');
174+
expect(element.href).toContain(originalHref);
175+
});
176+
177+
it('should process multiple elements correctly', async () => {
178+
// Create multiple elements
179+
const element1 = document.createElement('a');
180+
const element2 = document.createElement('a');
181+
const element3 = document.createElement('a');
182+
183+
const parent1 = document.createElement('div');
184+
const parent2 = document.createElement('div');
185+
const parent3 = document.createElement('div');
186+
187+
parent1.appendChild(element1);
188+
parent2.appendChild(element2);
189+
parent3.appendChild(element3);
190+
191+
document.body.appendChild(parent1);
192+
document.body.appendChild(parent2);
193+
document.body.appendChild(parent3);
194+
195+
// Setup first element (should be processed)
196+
element1.setAttribute('data-wcs-osi', 'vQmS1H18A6_kPd0tYBgKnp-TQIF0GbT6p8SH8rWcLMs');
197+
element1.setAttribute('data-modal', 'crm');
198+
element1.href = 'href1';
199+
200+
// Setup second element (should not be processed - wrong modal type)
201+
element2.setAttribute('data-wcs-osi', 'ZZQMV2cU-SWQoDxuznonUFMRdxSyTr4J3fB77YBNakY');
202+
element2.setAttribute('data-modal', 'other');
203+
element2.href = 'href2';
204+
205+
// Setup third element (should be processed)
206+
element3.setAttribute('data-wcs-osi', 'vV01ci-KLH6hYdRfUKMBFx009hdpxZcIRG1-BY_PutE');
207+
element3.setAttribute('data-modal', 'crm');
208+
element3.href = 'href3';
209+
210+
await threeInOne();
211+
212+
const processedElement1 = parent1.querySelector('a');
213+
const processedElement2 = parent2.querySelector('a');
214+
const processedElement3 = parent3.querySelector('a');
215+
216+
// First element should be processed
217+
expect(processedElement1.getAttribute('data-modal')).toBeNull();
218+
expect(processedElement1.getAttribute('data-checkout-workflow-step')).toBe('email');
219+
expect(processedElement1.href).toContain('commerce.adobe.com');
220+
221+
// Second element should not be processed
222+
expect(processedElement2.getAttribute('data-modal')).toBe('other');
223+
expect(processedElement2.getAttribute('data-checkout-workflow-step')).toBeNull();
224+
expect(processedElement2.href).toContain('href2');
225+
226+
// Third element should be processed
227+
expect(processedElement3.getAttribute('data-modal')).toBeNull();
228+
expect(processedElement3.getAttribute('data-checkout-workflow-step')).toBe('email');
229+
expect(processedElement3.href).toContain('commerce.adobe.com');
230+
});
231+
232+
it('should clone element and wait for threeInOneReady class', async () => {
233+
mockElement.setAttribute('data-wcs-osi', 'vQmS1H18A6_kPd0tYBgKnp-TQIF0GbT6p8SH8rWcLMs');
234+
mockElement.setAttribute('data-modal', 'crm');
235+
mockElement.classList.add('original-class');
236+
237+
// Mock cloneNode to track calls
238+
const originalCloneNode = mockElement.cloneNode;
239+
const mockClone = document.createElement('a');
240+
const mockContains = jest.fn().mockReturnValue(false);
241+
mockClone.classList = {
242+
contains: mockContains,
243+
add: jest.fn(),
244+
};
245+
mockElement.cloneNode = jest.fn().mockReturnValue(mockClone);
246+
247+
await threeInOne();
248+
249+
expect(mockElement.cloneNode).toHaveBeenCalledWith(true);
250+
251+
// Simulate the clone getting the threeInOneReady class
252+
mockContains.mockReturnValue(true);
253+
jest.advanceTimersByTime(100);
254+
255+
// Restore original method
256+
mockElement.cloneNode = originalCloneNode;
257+
});
258+
259+
it('should handle elements without parent', async () => {
260+
// Create an element without a parent
261+
const orphanElement = document.createElement('a');
262+
// Use an unknown offer ID so it doesn't try to replace the element
263+
orphanElement.setAttribute('data-wcs-osi', 'unknown-offer-id');
264+
orphanElement.setAttribute('data-modal', 'crm');
265+
266+
// Add to document but remove parent reference
267+
document.body.appendChild(orphanElement);
268+
Object.defineProperty(orphanElement, 'parentElement', {
269+
value: null,
270+
writable: true,
271+
});
272+
273+
// Should not throw error since unknown offer ID won't trigger replaceChild
274+
await expect(threeInOne()).resolves.not.toThrow();
275+
});
276+
277+
it('should throw error when trying to replace element with null parent', async () => {
278+
// Create an element without a parent
279+
const orphanElement = document.createElement('a');
280+
// Use a known offer ID that will trigger replaceChild
281+
orphanElement.setAttribute('data-wcs-osi', 'vQmS1H18A6_kPd0tYBgKnp-TQIF0GbT6p8SH8rWcLMs');
282+
orphanElement.setAttribute('data-modal', 'crm');
283+
284+
// Add to document but remove parent reference
285+
document.body.appendChild(orphanElement);
286+
Object.defineProperty(orphanElement, 'parentElement', {
287+
value: null,
288+
writable: true,
289+
});
290+
291+
// Should throw error when trying to replaceChild on null parent
292+
await expect(threeInOne()).rejects.toThrow();
293+
});
294+
});
295+
296+
describe('offerMap integration', () => {
297+
it('should use correct URLs from offerMap', async () => {
298+
const testCases = [
299+
{
300+
offerId: 'vQmS1H18A6_kPd0tYBgKnp-TQIF0GbT6p8SH8rWcLMs',
301+
expectedPath: '/store/commitment?items%5B0%5D%5Bid%5D=7C30A05FE0EC0BA92566737E720C4692&cli=doc_cloud&ctx=fp&co=US&lang=en',
302+
},
303+
{
304+
offerId: 'vV01ci-KLH6hYdRfUKMBFx009hdpxZcIRG1-BY_PutE',
305+
expectedPath: '/store/email?items%5B0%5D%5Bid%5D=4F5EFB5713F74AFFC5960C031FB24656&items%5B0%5D%5Bq%5D=2&cli=doc_cloud&ctx=fp&co=US&lang=en',
306+
},
307+
];
308+
309+
for (const testCase of testCases) {
310+
// Clear previous elements
311+
document.body.innerHTML = '';
312+
313+
const parent = document.createElement('div');
314+
const element = document.createElement('a');
315+
parent.appendChild(element);
316+
document.body.appendChild(parent);
317+
318+
element.setAttribute('data-wcs-osi', testCase.offerId);
319+
element.setAttribute('data-modal', 'crm');
320+
321+
await threeInOne();
322+
323+
const processedElement = parent.querySelector('a');
324+
const expectedHref = `${mockCommerceOrigin}${testCase.expectedPath}`;
325+
expect(processedElement.href).toBe(expectedHref);
326+
}
327+
});
328+
});
329+
});

0 commit comments

Comments
 (0)