import { createScript, createLink } from '../src/dom';
describe('createScript', () => {
afterEach(() => {
document.getElementsByTagName('html')[0].innerHTML = '';
});
it('should create a new script element if one does not exist', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { script, needAttach } = createScript({ url, cb });
expect(script.tagName).toBe('SCRIPT');
expect(script.src).toBe(url);
expect(needAttach).toBe(true);
});
it('should reuse an existing script element if one exists', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
document.body.innerHTML = ``;
const { script, needAttach } = createScript({ url, cb });
expect(script.tagName).toBe('SCRIPT');
expect(script.src).toBe(url);
expect(needAttach).toBe(false);
});
it('should set attributes on the script element', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const attrs = { async: true, 'data-test': 'test' };
const { script } = createScript({ url, cb, attrs });
expect(script.async).toBe(true);
expect(script.getAttribute('data-test')).toBe('test');
});
it('should call the callback when the script loads', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { script, needAttach } = createScript({ url, cb });
if (needAttach) {
document.body.appendChild(script);
}
script?.onload?.(new Event('load'));
expect(cb).toHaveBeenCalled();
});
it('should call the callback when the script times out', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
createScript({
url,
cb,
attrs: {},
createScriptHook: () => ({ timeout: 100 }),
});
setTimeout(() => {
expect(cb).toHaveBeenCalled();
}, 150);
});
describe('Timeout', () => {
beforeEach(() => {
jest.spyOn(global, 'setTimeout');
jest.spyOn(global, 'clearTimeout'); // Add this line
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should use the default timeout of 20000ms if no timeout is specified', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { script } = createScript({ url, cb });
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 20000);
});
it('should use the timeout specified in the createScriptHook', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const customTimeout = 5000;
createScript({
url,
cb,
attrs: {},
createScriptHook: () => ({ timeout: customTimeout }),
});
expect(setTimeout).toHaveBeenCalledWith(
expect.any(Function),
customTimeout,
);
});
it('should clear the timeout when the script loads successfully', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { script, needAttach } = createScript({ url, cb });
if (needAttach) {
document.body.appendChild(script);
}
script?.onload?.(new Event('load'));
expect(clearTimeout).toHaveBeenCalled();
});
it('should clear the timeout when the script fails to load', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { script, needAttach } = createScript({ url, cb });
if (needAttach) {
document.body.appendChild(script);
}
script?.onerror?.(new Event('error'));
expect(clearTimeout).toHaveBeenCalled();
});
it('should set section attributes on the script element', () => {
const url = 'https://example.com/script.js';
const scriptHookUrl = 'https://example.com/hook-script.js';
const cb = jest.fn();
const attrs = {
async: true,
'data-test': 'test',
crossOrigin: 'anonymous',
};
const { script } = createScript({
url,
cb,
attrs,
createScriptHook: (url) => {
const scriptEle = document.createElement('script');
scriptEle.src = scriptHookUrl;
scriptEle.crossOrigin = 'use-credentials';
scriptEle.async = false;
return scriptEle;
},
});
// if user return element by createScriptHook, it will not add default attrs
expect(script.src).toBe(scriptHookUrl);
expect(script.async).toBe(false);
expect(script.crossOrigin).toBe('use-credentials');
expect(script.getAttribute('data-test')).toBe(null);
});
});
});
describe('createLink', () => {
afterEach(() => {
document.getElementsByTagName('html')[0].innerHTML = '';
});
it('should create a new link element if one does not exist', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { link, needAttach } = createLink({
url,
cb,
attrs: { as: 'script' },
});
expect(link.tagName).toBe('LINK');
expect(link.href).toBe(url);
expect(link.getAttribute('as')).toBe('script');
expect(needAttach).toBe(true);
});
xit('should reuse an existing link element if one exists', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
document.head.innerHTML = ``;
const { link, needAttach } = createLink({
url,
cb,
attrs: {
rel: 'preload',
as: 'script',
},
});
expect(link.tagName).toBe('LINK');
expect(link.href).toBe(url);
expect(needAttach).toBe(false);
});
it('should set attributes on the link element', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const attrs = { rel: 'preload', as: 'script', 'data-test': 'test' };
const { link } = createLink({ url, cb, attrs });
expect(link.rel).toBe('preload');
expect(link.getAttribute('as')).toBe('script');
expect(link.getAttribute('data-test')).toBe('test');
});
it('should set section attributes on the link element', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const attrs = {
rel: 'preload',
as: 'script',
'data-test': 'test',
crossOrigin: 'anonymous',
};
const { link } = createLink({
url,
cb,
attrs,
createLinkHook: (url) => {
const linkEle = document.createElement('link');
linkEle.href = url;
linkEle.crossOrigin = 'use-credentials';
return linkEle;
},
});
// if user return element by createLinkHook, it will not add default attrs
expect(link.rel).toBe('');
expect(link.crossOrigin).toBe('use-credentials');
expect(link.getAttribute('as')).toBe(null);
expect(link.getAttribute('data-test')).toBe(null);
});
it('should call the callback when the link loads', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { link, needAttach } = createLink({
url,
cb,
attrs: { as: 'script' },
});
if (needAttach) {
document.head.appendChild(link);
}
link?.onload?.(new Event('load'));
expect(cb).toHaveBeenCalled();
});
it('should call the callback when the link fails to load', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const { link, needAttach } = createLink({
url,
cb,
attrs: { as: 'script' },
});
if (needAttach) {
document.head.appendChild(link);
}
link?.onerror?.(new Event('error'));
expect(cb).toHaveBeenCalled();
});
it('should use the link element returned by createLinkHook', () => {
const url = 'https://example.com/script.js';
const cb = jest.fn();
const customLink = document.createElement('link');
customLink.href = url;
customLink.rel = 'preload';
customLink.setAttribute('as', 'script');
const { link } = createLink({
url,
cb,
attrs: {},
createLinkHook: () => customLink,
});
expect(link).toBe(customLink);
});
});