-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathexportImport.test.js
More file actions
161 lines (142 loc) · 5.94 KB
/
Copy pathexportImport.test.js
File metadata and controls
161 lines (142 loc) · 5.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
const { TextEncoder, TextDecoder } = require('util');
global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;
const { JSDOM } = require('jsdom');
describe('export and import logic', () => {
let dom;
let exportWebhooks;
let handleImport;
beforeEach(() => {
dom = new JSDOM(`<!DOCTYPE html><html><body>
<button type="button" id="add-new-webhook-btn"></button>
<button type="button" id="manage-groups-btn">Manage Groups</button>
<form id="add-webhook-form" class="hidden">
<input id="webhook-label" />
<select id="webhook-group">
<option value="" selected>No Group</option>
</select>
<input id="webhook-url" />
<select id="webhook-method"></select>
<input id="webhook-identifier" />
<div id="headers-list"></div>
<input id="header-key" />
<input id="header-value" />
<button type="button" id="add-header-btn"></button>
<div class="collapsible-header" id="url-filter-header">
<button type="button" id="toggle-url-filter" class="toggle-btn" aria-expanded="false">
<span class="toggle-icon">+</span>
</button>
</div>
<div id="url-filter-content" class="collapsible-content collapsed">
<input id="webhook-url-filter" />
</div>
<div class="collapsible-header" id="custom-payload-header">
<button type="button" id="toggle-custom-payload" class="toggle-btn" aria-expanded="false">
<span class="toggle-icon">+</span>
</button>
</div>
<div id="custom-payload-content" class="collapsible-content collapsed">
<textarea id="webhook-custom-payload"></textarea>
<div id="variables-autocomplete" class="autocomplete-container hidden"></div>
</div>
<button type="button" id="cancel-edit-btn" class="hidden"></button>
<button type="submit"></button>
<button type="button" id="test-webhook-btn" class="hidden">__MSG_optionsTestButton__</button>
<p id="form-status-message" class="status-message"></p>
</form>
<!-- Group Management Modal -->
<div id="manage-groups-modal" class="modal hidden">
<div class="modal-content">
<span class="close-manage-groups">×</span>
<h2>Manage Groups</h2>
<div class="form-group">
<label for="new-group-name">New Group Name:</label>
<input type="text" id="new-group-name" placeholder="Enter new group name" />
<button type="button" id="add-group-btn">Add Group</button>
</div>
<div id="existing-groups-container">
<h3>Existing Groups:</h3>
<ul id="groups-list"></ul>
</div>
<div class="modal-actions">
<button type="button" id="close-manage-groups-btn">Close</button>
</div>
</div>
</div>
<div id="import-export-actions">
<button type="button" id="export-webhooks-btn"></button>
<button type="button" id="import-webhooks-btn"></button>
<input type="file" id="import-webhooks-input" />
</div>
<ul id="webhook-list"></ul>
<p id="no-webhooks-message" class="hidden"></p>
</body></html>`);
global.document = dom.window.document;
global.window = dom.window;
global.Node = dom.window.Node;
global.replaceI18nPlaceholders = jest.fn();
global.browser = {
storage: {
sync: {
get: jest.fn().mockResolvedValue({ webhooks: [] }),
set: jest.fn(),
},
},
i18n: { getMessage: jest.fn((k) => k) },
};
global.window.getBrowserAPI = jest.fn().mockReturnValue(global.browser);
const originalAddEventListener = dom.window.document.addEventListener;
let domContentLoadedHandler;
dom.window.document.addEventListener = jest.fn((event, handler) => {
if (event === 'DOMContentLoaded') {
domContentLoadedHandler = handler;
} else {
originalAddEventListener.call(dom.window.document, event, handler);
}
});
const module = require('../options/options.js');
exportWebhooks = module.exportWebhooks;
handleImport = module.handleImport;
if (domContentLoadedHandler) {
domContentLoadedHandler();
}
global.URL.createObjectURL = jest.fn().mockReturnValue('blob:url');
});
afterEach(() => {
jest.resetModules();
dom.window.close();
delete global.document;
delete global.window.getBrowserAPI;
delete global.window;
delete global.Node;
delete global.browser;
delete global.replaceI18nPlaceholders;
delete global.URL.createObjectURL;
});
test('exportWebhooks creates downloadable file with stored hooks', async () => {
const hooks = [{ id: '1', label: 'A', url: 'https://example.com' }];
global.browser.storage.sync.get.mockResolvedValue({ webhooks: hooks });
const clickSpy = jest.fn();
const originalCreateElement = dom.window.document.createElement.bind(dom.window.document);
jest.spyOn(dom.window.document, 'createElement').mockImplementation((tag) => {
if (tag === 'a') {
return { href: '', download: '', click: clickSpy };
}
return originalCreateElement(tag);
});
await exportWebhooks();
expect(global.URL.createObjectURL).toHaveBeenCalled();
const blob = global.URL.createObjectURL.mock.calls[0][0];
const text = await blob.text();
expect(JSON.parse(text)).toEqual({ webhooks: hooks });
expect(clickSpy).toHaveBeenCalled();
});
test('handleImport saves hooks from uploaded file', async () => {
const hooks = [{ id: '1', label: 'A', url: 'https://example.com' }];
const file = new File([JSON.stringify({ webhooks: hooks })], 'webhooks.json', { type: 'application/json' });
const event = { target: { files: [file], value: 'test' } };
await handleImport(event);
expect(global.browser.storage.sync.set).toHaveBeenCalledWith({ webhooks: hooks });
expect(event.target.value).toBe('');
});
});