onairos 2.2.1 → 2.3.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/LARAVEL_INTEGRATION_GUIDE.md +643 -0
- package/LARAVEL_TECHNICAL_EXPLANATION.md +465 -0
- package/README.md +122 -43
- package/dist/onairos-laravel.js +2 -0
- package/dist/onairos-laravel.js.map +1 -0
- package/laravel.txt +430 -0
- package/package.json +31 -2
- package/src/laravel/OnairosVue.vue +398 -0
- package/src/laravel/blade-helpers.js +263 -0
- package/src/laravel/vite-plugin.js +179 -0
- package/tests/laravel/examples/blade-example.test.js +283 -0
- package/tests/laravel/laravel-integration.test.js +647 -0
- package/tests/laravel/setup.js +84 -0
- package/tests/laravel/vitest.config.js +20 -0
- package/webpack.config.js +46 -13
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Laravel Integration Test Suite
|
|
3
|
+
*
|
|
4
|
+
* Tests the Laravel-specific functionality of the Onairos package
|
|
5
|
+
* including Blade helpers, Vue components, and Vite plugins.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
|
+
import { JSDOM } from 'jsdom';
|
|
10
|
+
import { createApp } from 'vue';
|
|
11
|
+
import { mount } from '@vue/test-utils';
|
|
12
|
+
|
|
13
|
+
// Import Laravel-specific modules
|
|
14
|
+
import {
|
|
15
|
+
initializeOnairosForBlade,
|
|
16
|
+
createOnairosButton,
|
|
17
|
+
renderOnairosDirective
|
|
18
|
+
} from '../../src/laravel/blade-helpers.js';
|
|
19
|
+
|
|
20
|
+
import OnairosVue from '../../src/laravel/OnairosVue.vue';
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
onairosLaravelPlugin,
|
|
24
|
+
onairosVuePlugin,
|
|
25
|
+
onairosReactPlugin
|
|
26
|
+
} from '../../src/laravel/vite-plugin.js';
|
|
27
|
+
|
|
28
|
+
// Mock DOM environment
|
|
29
|
+
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
30
|
+
global.window = dom.window;
|
|
31
|
+
global.document = dom.window.document;
|
|
32
|
+
global.navigator = dom.window.navigator;
|
|
33
|
+
|
|
34
|
+
describe('Laravel Blade Integration', () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
// Reset DOM
|
|
37
|
+
document.body.innerHTML = '';
|
|
38
|
+
|
|
39
|
+
// Reset global objects
|
|
40
|
+
delete window.OnairosConfig;
|
|
41
|
+
delete window.OnairosUtils;
|
|
42
|
+
delete window.createOnairosButton;
|
|
43
|
+
delete window.initializeOnairosForBlade;
|
|
44
|
+
|
|
45
|
+
// Mock console methods
|
|
46
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
47
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
vi.restoreAllMocks();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('initializeOnairosForBlade', () => {
|
|
55
|
+
test('should initialize with default configuration', () => {
|
|
56
|
+
initializeOnairosForBlade();
|
|
57
|
+
|
|
58
|
+
expect(window.OnairosConfig).toBeDefined();
|
|
59
|
+
expect(window.OnairosConfig.baseUrl).toBe('https://api2.onairos.uk');
|
|
60
|
+
expect(window.OnairosConfig.testMode).toBe(false);
|
|
61
|
+
expect(window.OnairosConfig.autoDetectMobile).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should merge custom configuration', () => {
|
|
65
|
+
const customConfig = {
|
|
66
|
+
apiKey: 'test-api-key',
|
|
67
|
+
testMode: true,
|
|
68
|
+
baseUrl: 'https://custom.api.com'
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
initializeOnairosForBlade(customConfig);
|
|
72
|
+
|
|
73
|
+
expect(window.OnairosConfig.apiKey).toBe('test-api-key');
|
|
74
|
+
expect(window.OnairosConfig.testMode).toBe(true);
|
|
75
|
+
expect(window.OnairosConfig.baseUrl).toBe('https://custom.api.com');
|
|
76
|
+
expect(window.OnairosConfig.autoDetectMobile).toBe(true); // Default preserved
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('should inject global styles when enabled', () => {
|
|
80
|
+
initializeOnairosForBlade({ globalStyles: true });
|
|
81
|
+
|
|
82
|
+
const styleElement = document.getElementById('onairos-styles');
|
|
83
|
+
expect(styleElement).not.toBeNull();
|
|
84
|
+
expect(styleElement.textContent).toContain('.onairos-btn');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('should not inject styles when disabled', () => {
|
|
88
|
+
initializeOnairosForBlade({ globalStyles: false });
|
|
89
|
+
|
|
90
|
+
const styleElement = document.getElementById('onairos-styles');
|
|
91
|
+
expect(styleElement).toBeNull();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('should setup mobile detection utilities', () => {
|
|
95
|
+
initializeOnairosForBlade();
|
|
96
|
+
|
|
97
|
+
expect(window.OnairosUtils).toBeDefined();
|
|
98
|
+
expect(typeof window.OnairosUtils.detectMobile).toBe('function');
|
|
99
|
+
expect(typeof window.OnairosUtils.isMobile).toBe('boolean');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('createOnairosButton', () => {
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
initializeOnairosForBlade();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('should create button in target element', () => {
|
|
109
|
+
// Create target element
|
|
110
|
+
const targetDiv = document.createElement('div');
|
|
111
|
+
targetDiv.id = 'test-button';
|
|
112
|
+
document.body.appendChild(targetDiv);
|
|
113
|
+
|
|
114
|
+
createOnairosButton('test-button', {
|
|
115
|
+
requestData: ['email', 'profile'],
|
|
116
|
+
webpageName: 'Test App'
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(targetDiv.innerHTML).toContain('onairos-button-container');
|
|
120
|
+
expect(targetDiv.innerHTML).toContain('Connect with Onairos');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('should handle missing target element gracefully', () => {
|
|
124
|
+
const consoleSpy = vi.spyOn(console, 'error');
|
|
125
|
+
|
|
126
|
+
createOnairosButton('non-existent-element', {});
|
|
127
|
+
|
|
128
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
129
|
+
expect.stringContaining('Element with ID "non-existent-element" not found')
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('should apply custom configuration', () => {
|
|
134
|
+
const targetDiv = document.createElement('div');
|
|
135
|
+
targetDiv.id = 'custom-button';
|
|
136
|
+
document.body.appendChild(targetDiv);
|
|
137
|
+
|
|
138
|
+
createOnairosButton('custom-button', {
|
|
139
|
+
requestData: ['email'],
|
|
140
|
+
webpageName: 'Custom App',
|
|
141
|
+
buttonType: 'icon',
|
|
142
|
+
textColor: 'blue'
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const button = document.getElementById('custom-button-btn');
|
|
146
|
+
expect(button).not.toBeNull();
|
|
147
|
+
expect(button.className).toContain('onairos-btn-icon');
|
|
148
|
+
|
|
149
|
+
const textSpan = button.querySelector('.onairos-btn-text');
|
|
150
|
+
expect(textSpan.style.color).toBe('blue');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('should add click event listener', () => {
|
|
154
|
+
const targetDiv = document.createElement('div');
|
|
155
|
+
targetDiv.id = 'clickable-button';
|
|
156
|
+
document.body.appendChild(targetDiv);
|
|
157
|
+
|
|
158
|
+
createOnairosButton('clickable-button', {
|
|
159
|
+
requestData: ['email'],
|
|
160
|
+
webpageName: 'Clickable App'
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const button = document.getElementById('clickable-button-btn');
|
|
164
|
+
expect(button).not.toBeNull();
|
|
165
|
+
|
|
166
|
+
// Verify button has click event (we can't easily test the actual click without complex mocking)
|
|
167
|
+
expect(button.onclick).not.toBeNull();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('renderOnairosDirective', () => {
|
|
172
|
+
test('should generate HTML with unique ID', () => {
|
|
173
|
+
const html1 = renderOnairosDirective({ requestData: ['email'] });
|
|
174
|
+
const html2 = renderOnairosDirective({ requestData: ['profile'] });
|
|
175
|
+
|
|
176
|
+
expect(html1).toContain('<div id="onairos-');
|
|
177
|
+
expect(html2).toContain('<div id="onairos-');
|
|
178
|
+
|
|
179
|
+
// Extract IDs to ensure they're unique
|
|
180
|
+
const id1 = html1.match(/id="(onairos-[^"]+)"/)[1];
|
|
181
|
+
const id2 = html2.match(/id="(onairos-[^"]+)"/)[1];
|
|
182
|
+
|
|
183
|
+
expect(id1).not.toBe(id2);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('should include configuration in script', () => {
|
|
187
|
+
const options = {
|
|
188
|
+
requestData: ['email', 'profile'],
|
|
189
|
+
webpageName: 'Directive Test'
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const html = renderOnairosDirective(options);
|
|
193
|
+
|
|
194
|
+
expect(html).toContain('createOnairosButton');
|
|
195
|
+
expect(html).toContain(JSON.stringify(options));
|
|
196
|
+
expect(html).toContain('DOMContentLoaded');
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('Mobile Detection', () => {
|
|
201
|
+
beforeEach(() => {
|
|
202
|
+
initializeOnairosForBlade();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('should detect mobile user agents', () => {
|
|
206
|
+
// Mock mobile user agent
|
|
207
|
+
Object.defineProperty(window.navigator, 'userAgent', {
|
|
208
|
+
writable: true,
|
|
209
|
+
value: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)'
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const isMobile = window.OnairosUtils.detectMobile();
|
|
213
|
+
expect(isMobile).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('should detect desktop user agents', () => {
|
|
217
|
+
// Mock desktop user agent
|
|
218
|
+
Object.defineProperty(window.navigator, 'userAgent', {
|
|
219
|
+
writable: true,
|
|
220
|
+
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Mock window dimensions for desktop
|
|
224
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
225
|
+
writable: true,
|
|
226
|
+
value: 1920
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const isMobile = window.OnairosUtils.detectMobile();
|
|
230
|
+
expect(isMobile).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test('should detect mobile by screen width', () => {
|
|
234
|
+
// Mock narrow screen
|
|
235
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
236
|
+
writable: true,
|
|
237
|
+
value: 500
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const isMobile = window.OnairosUtils.detectMobile();
|
|
241
|
+
expect(isMobile).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('Laravel Vue Integration', () => {
|
|
247
|
+
describe('OnairosVue Component', () => {
|
|
248
|
+
test('should render with default props', () => {
|
|
249
|
+
const wrapper = mount(OnairosVue);
|
|
250
|
+
|
|
251
|
+
expect(wrapper.find('.onairos-vue-wrapper').exists()).toBe(true);
|
|
252
|
+
expect(wrapper.find('.onairos-vue-btn').exists()).toBe(true);
|
|
253
|
+
expect(wrapper.text()).toContain('Connect with Onairos');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('should accept custom props', () => {
|
|
257
|
+
const wrapper = mount(OnairosVue, {
|
|
258
|
+
props: {
|
|
259
|
+
requestData: ['email', 'profile', 'preferences'],
|
|
260
|
+
webpageName: 'Vue Test App',
|
|
261
|
+
buttonType: 'rounded',
|
|
262
|
+
size: 'large',
|
|
263
|
+
textColor: 'black'
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(wrapper.vm.requestData).toEqual(['email', 'profile', 'preferences']);
|
|
268
|
+
expect(wrapper.vm.webpageName).toBe('Vue Test App');
|
|
269
|
+
expect(wrapper.vm.buttonType).toBe('rounded');
|
|
270
|
+
expect(wrapper.vm.size).toBe('large');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('should validate prop types', () => {
|
|
274
|
+
// Test invalid buttonType
|
|
275
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
276
|
+
|
|
277
|
+
mount(OnairosVue, {
|
|
278
|
+
props: {
|
|
279
|
+
buttonType: 'invalid-type'
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Vue should warn about invalid prop value
|
|
284
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('should apply correct CSS classes', () => {
|
|
288
|
+
const wrapper = mount(OnairosVue, {
|
|
289
|
+
props: {
|
|
290
|
+
buttonType: 'pill',
|
|
291
|
+
size: 'large',
|
|
292
|
+
customClass: 'my-custom-class'
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const button = wrapper.find('.onairos-vue-btn');
|
|
297
|
+
expect(button.classes()).toContain('onairos-btn-pill');
|
|
298
|
+
expect(button.classes()).toContain('onairos-btn-large');
|
|
299
|
+
expect(button.classes()).toContain('my-custom-class');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('should handle loading state', async () => {
|
|
303
|
+
const wrapper = mount(OnairosVue);
|
|
304
|
+
|
|
305
|
+
// Initially not loading
|
|
306
|
+
expect(wrapper.find('.onairos-loading').exists()).toBe(false);
|
|
307
|
+
expect(wrapper.find('.onairos-vue-btn').exists()).toBe(true);
|
|
308
|
+
|
|
309
|
+
// Set loading state
|
|
310
|
+
await wrapper.setData({ isLoading: true });
|
|
311
|
+
|
|
312
|
+
expect(wrapper.find('.onairos-loading').exists()).toBe(true);
|
|
313
|
+
expect(wrapper.find('.onairos-vue-btn').exists()).toBe(false);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test('should emit events on click', async () => {
|
|
317
|
+
const wrapper = mount(OnairosVue);
|
|
318
|
+
|
|
319
|
+
await wrapper.find('.onairos-vue-btn').trigger('click');
|
|
320
|
+
|
|
321
|
+
expect(wrapper.emitted()).toHaveProperty('click');
|
|
322
|
+
expect(wrapper.emitted()).toHaveProperty('loading');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test('should show success message', async () => {
|
|
326
|
+
const wrapper = mount(OnairosVue, {
|
|
327
|
+
props: {
|
|
328
|
+
successMessage: 'Custom success message'
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
await wrapper.setData({ showSuccess: true });
|
|
333
|
+
|
|
334
|
+
const successDiv = wrapper.find('.onairos-success');
|
|
335
|
+
expect(successDiv.exists()).toBe(true);
|
|
336
|
+
expect(successDiv.text()).toContain('Custom success message');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('should show error message', async () => {
|
|
340
|
+
const wrapper = mount(OnairosVue);
|
|
341
|
+
|
|
342
|
+
await wrapper.setData({ error: 'Connection failed' });
|
|
343
|
+
|
|
344
|
+
const errorDiv = wrapper.find('.onairos-error');
|
|
345
|
+
expect(errorDiv.exists()).toBe(true);
|
|
346
|
+
expect(errorDiv.text()).toContain('Connection failed');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test('should disable button when disabled prop is true', () => {
|
|
350
|
+
const wrapper = mount(OnairosVue, {
|
|
351
|
+
props: {
|
|
352
|
+
disabled: true
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const button = wrapper.find('.onairos-vue-btn');
|
|
357
|
+
expect(button.attributes('disabled')).toBeDefined();
|
|
358
|
+
expect(button.classes()).toContain('onairos-btn-disabled');
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
describe('Laravel Vite Plugin Integration', () => {
|
|
364
|
+
describe('onairosLaravelPlugin', () => {
|
|
365
|
+
test('should create plugin with default options', () => {
|
|
366
|
+
const plugin = onairosLaravelPlugin();
|
|
367
|
+
|
|
368
|
+
expect(plugin).toBeDefined();
|
|
369
|
+
expect(plugin.name).toBe('onairos-laravel');
|
|
370
|
+
expect(typeof plugin.config).toBe('function');
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test('should accept custom options', () => {
|
|
374
|
+
const customOptions = {
|
|
375
|
+
autoImport: false,
|
|
376
|
+
bladeSupport: false,
|
|
377
|
+
enableHMR: false
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const plugin = onairosLaravelPlugin(customOptions);
|
|
381
|
+
expect(plugin).toBeDefined();
|
|
382
|
+
expect(plugin.name).toBe('onairos-laravel');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('should configure Vite properly', () => {
|
|
386
|
+
const plugin = onairosLaravelPlugin({
|
|
387
|
+
optimizeDeps: true,
|
|
388
|
+
bladeSupport: true
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const mockViteConfig = {
|
|
392
|
+
optimizeDeps: { include: [] },
|
|
393
|
+
resolve: { alias: {} },
|
|
394
|
+
server: { watch: { include: [] } }
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// Simulate Vite calling the config function
|
|
398
|
+
plugin.config(mockViteConfig, { command: 'serve' });
|
|
399
|
+
|
|
400
|
+
expect(mockViteConfig.optimizeDeps.include).toContain('onairos');
|
|
401
|
+
expect(mockViteConfig.resolve.alias['@onairos']).toBeDefined();
|
|
402
|
+
expect(mockViteConfig.server.watch.include).toContain('resources/views/**/*.blade.php');
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe('onairosVuePlugin', () => {
|
|
407
|
+
test('should create Vue-specific plugin', () => {
|
|
408
|
+
const plugin = onairosVuePlugin();
|
|
409
|
+
|
|
410
|
+
expect(plugin).toBeDefined();
|
|
411
|
+
expect(plugin.name).toBe('onairos-vue-laravel');
|
|
412
|
+
expect(typeof plugin.config).toBe('function');
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test('should configure Vue dependencies', () => {
|
|
416
|
+
const plugin = onairosVuePlugin();
|
|
417
|
+
const mockViteConfig = {
|
|
418
|
+
optimizeDeps: { include: [] }
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
plugin.config(mockViteConfig);
|
|
422
|
+
|
|
423
|
+
expect(mockViteConfig.optimizeDeps.include).toContain('onairos');
|
|
424
|
+
expect(mockViteConfig.optimizeDeps.include).toContain('vue');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test('should handle auto-import for Vue files', () => {
|
|
428
|
+
const plugin = onairosVuePlugin({ autoImport: true });
|
|
429
|
+
|
|
430
|
+
const codeWithOnairosButton = '<template><OnairosButton /></template>';
|
|
431
|
+
const codeWithoutImport = 'export default {}';
|
|
432
|
+
|
|
433
|
+
// Mock transform function
|
|
434
|
+
const result1 = plugin.transform(codeWithOnairosButton, 'Component.vue');
|
|
435
|
+
const result2 = plugin.transform(codeWithoutImport, 'Other.vue');
|
|
436
|
+
|
|
437
|
+
expect(result1).toContain("import { OnairosButton } from 'onairos'");
|
|
438
|
+
expect(result2).toBeNull(); // No transform needed
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe('onairosReactPlugin', () => {
|
|
443
|
+
test('should create React-specific plugin', () => {
|
|
444
|
+
const plugin = onairosReactPlugin();
|
|
445
|
+
|
|
446
|
+
expect(plugin).toBeDefined();
|
|
447
|
+
expect(plugin.name).toBe('onairos-react-laravel');
|
|
448
|
+
expect(typeof plugin.config).toBe('function');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test('should configure React dependencies', () => {
|
|
452
|
+
const plugin = onairosReactPlugin();
|
|
453
|
+
const mockViteConfig = {
|
|
454
|
+
optimizeDeps: { include: [] }
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
plugin.config(mockViteConfig);
|
|
458
|
+
|
|
459
|
+
expect(mockViteConfig.optimizeDeps.include).toContain('onairos');
|
|
460
|
+
expect(mockViteConfig.optimizeDeps.include).toContain('react');
|
|
461
|
+
expect(mockViteConfig.optimizeDeps.include).toContain('react-dom');
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test('should handle auto-import for React files', () => {
|
|
465
|
+
const plugin = onairosReactPlugin({ autoImport: true });
|
|
466
|
+
|
|
467
|
+
const jsxCode = 'function App() { return <OnairosButton />; }';
|
|
468
|
+
const tsxCode = 'const App: React.FC = () => <OnairosButton />;';
|
|
469
|
+
const regularJs = 'console.log("hello");';
|
|
470
|
+
|
|
471
|
+
const result1 = plugin.transform(jsxCode, 'App.jsx');
|
|
472
|
+
const result2 = plugin.transform(tsxCode, 'App.tsx');
|
|
473
|
+
const result3 = plugin.transform(regularJs, 'utils.js');
|
|
474
|
+
|
|
475
|
+
expect(result1).toContain("import { OnairosButton } from 'onairos'");
|
|
476
|
+
expect(result2).toContain("import { OnairosButton } from 'onairos'");
|
|
477
|
+
expect(result3).toBeNull();
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
describe('Laravel Integration - End-to-End Scenarios', () => {
|
|
483
|
+
beforeEach(() => {
|
|
484
|
+
// Reset environment
|
|
485
|
+
document.body.innerHTML = '';
|
|
486
|
+
delete window.OnairosConfig;
|
|
487
|
+
delete window.OnairosUtils;
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('should support complete Blade integration workflow', () => {
|
|
491
|
+
// 1. Initialize Onairos for Blade
|
|
492
|
+
initializeOnairosForBlade({
|
|
493
|
+
testMode: true,
|
|
494
|
+
webpageName: 'E2E Test App'
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// 2. Create button element
|
|
498
|
+
const buttonContainer = document.createElement('div');
|
|
499
|
+
buttonContainer.id = 'e2e-button';
|
|
500
|
+
document.body.appendChild(buttonContainer);
|
|
501
|
+
|
|
502
|
+
// 3. Create Onairos button
|
|
503
|
+
createOnairosButton('e2e-button', {
|
|
504
|
+
requestData: ['email', 'profile'],
|
|
505
|
+
webpageName: 'E2E Test',
|
|
506
|
+
buttonType: 'pill'
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// 4. Verify complete setup
|
|
510
|
+
expect(window.OnairosConfig).toBeDefined();
|
|
511
|
+
expect(window.OnairosUtils).toBeDefined();
|
|
512
|
+
expect(document.getElementById('e2e-button-btn')).not.toBeNull();
|
|
513
|
+
expect(document.getElementById('onairos-styles')).not.toBeNull();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test('should handle multiple buttons on same page', () => {
|
|
517
|
+
initializeOnairosForBlade();
|
|
518
|
+
|
|
519
|
+
// Create multiple button containers
|
|
520
|
+
const container1 = document.createElement('div');
|
|
521
|
+
container1.id = 'button-1';
|
|
522
|
+
const container2 = document.createElement('div');
|
|
523
|
+
container2.id = 'button-2';
|
|
524
|
+
|
|
525
|
+
document.body.appendChild(container1);
|
|
526
|
+
document.body.appendChild(container2);
|
|
527
|
+
|
|
528
|
+
// Create multiple buttons
|
|
529
|
+
createOnairosButton('button-1', {
|
|
530
|
+
requestData: ['email'],
|
|
531
|
+
webpageName: 'App 1'
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
createOnairosButton('button-2', {
|
|
535
|
+
requestData: ['profile'],
|
|
536
|
+
webpageName: 'App 2'
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// Verify both buttons exist
|
|
540
|
+
expect(document.getElementById('button-1-btn')).not.toBeNull();
|
|
541
|
+
expect(document.getElementById('button-2-btn')).not.toBeNull();
|
|
542
|
+
|
|
543
|
+
// Verify they have different configurations
|
|
544
|
+
const btn1 = document.getElementById('button-1-btn');
|
|
545
|
+
const btn2 = document.getElementById('button-2-btn');
|
|
546
|
+
|
|
547
|
+
const config1 = JSON.parse(btn1.getAttribute('data-onairos-config'));
|
|
548
|
+
const config2 = JSON.parse(btn2.getAttribute('data-onairos-config'));
|
|
549
|
+
|
|
550
|
+
expect(config1.webpageName).toBe('App 1');
|
|
551
|
+
expect(config2.webpageName).toBe('App 2');
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
test('should handle initialization errors gracefully', () => {
|
|
555
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
556
|
+
|
|
557
|
+
// Try to create button without initialization
|
|
558
|
+
createOnairosButton('non-existent', {});
|
|
559
|
+
|
|
560
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
describe('Laravel Environment Integration', () => {
|
|
565
|
+
test('should work with Laravel .env variables', () => {
|
|
566
|
+
// Mock import.meta.env for Laravel Vite
|
|
567
|
+
global.import = {
|
|
568
|
+
meta: {
|
|
569
|
+
env: {
|
|
570
|
+
DEV: true,
|
|
571
|
+
VITE_ONAIROS_API_KEY: 'test-key',
|
|
572
|
+
VITE_ONAIROS_TEST_MODE: 'true',
|
|
573
|
+
VITE_ONAIROS_BASE_URL: 'https://test.api.com'
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
initializeOnairosForBlade({
|
|
579
|
+
testMode: global.import.meta.env.VITE_ONAIROS_TEST_MODE === 'true',
|
|
580
|
+
apiKey: global.import.meta.env.VITE_ONAIROS_API_KEY,
|
|
581
|
+
baseUrl: global.import.meta.env.VITE_ONAIROS_BASE_URL
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
expect(window.OnairosConfig.testMode).toBe(true);
|
|
585
|
+
expect(window.OnairosConfig.apiKey).toBe('test-key');
|
|
586
|
+
expect(window.OnairosConfig.baseUrl).toBe('https://test.api.com');
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
describe('Laravel Performance Tests', () => {
|
|
591
|
+
test('should initialize quickly', () => {
|
|
592
|
+
const startTime = performance.now();
|
|
593
|
+
|
|
594
|
+
initializeOnairosForBlade();
|
|
595
|
+
|
|
596
|
+
const endTime = performance.now();
|
|
597
|
+
const duration = endTime - startTime;
|
|
598
|
+
|
|
599
|
+
// Should initialize in less than 50ms
|
|
600
|
+
expect(duration).toBeLessThan(50);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test('should create buttons efficiently', () => {
|
|
604
|
+
initializeOnairosForBlade();
|
|
605
|
+
|
|
606
|
+
const container = document.createElement('div');
|
|
607
|
+
container.id = 'perf-test';
|
|
608
|
+
document.body.appendChild(container);
|
|
609
|
+
|
|
610
|
+
const startTime = performance.now();
|
|
611
|
+
|
|
612
|
+
createOnairosButton('perf-test', {
|
|
613
|
+
requestData: ['email'],
|
|
614
|
+
webpageName: 'Performance Test'
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const endTime = performance.now();
|
|
618
|
+
const duration = endTime - startTime;
|
|
619
|
+
|
|
620
|
+
// Should create button in less than 20ms
|
|
621
|
+
expect(duration).toBeLessThan(20);
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
test('should handle multiple buttons efficiently', () => {
|
|
625
|
+
initializeOnairosForBlade();
|
|
626
|
+
|
|
627
|
+
const startTime = performance.now();
|
|
628
|
+
|
|
629
|
+
// Create 10 buttons
|
|
630
|
+
for (let i = 0; i < 10; i++) {
|
|
631
|
+
const container = document.createElement('div');
|
|
632
|
+
container.id = `perf-button-${i}`;
|
|
633
|
+
document.body.appendChild(container);
|
|
634
|
+
|
|
635
|
+
createOnairosButton(`perf-button-${i}`, {
|
|
636
|
+
requestData: ['email'],
|
|
637
|
+
webpageName: `Perf Test ${i}`
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const endTime = performance.now();
|
|
642
|
+
const duration = endTime - startTime;
|
|
643
|
+
|
|
644
|
+
// Should create 10 buttons in less than 100ms
|
|
645
|
+
expect(duration).toBeLessThan(100);
|
|
646
|
+
});
|
|
647
|
+
});
|