larry babby and threejs for glsl

This commit is contained in:
Sam
2024-06-24 21:24:00 +12:00
parent 87d5dc634d
commit 907ebae4c0
6474 changed files with 1279596 additions and 8 deletions

View File

@@ -0,0 +1,41 @@
// @flow strict-local
import assert from 'assert';
import {DefaultMap} from '../src/DefaultMap';
describe('DefaultMap', () => {
it('constructs with entries just like Map', () => {
let map = new DefaultMap(
k => k,
[
[1, 3],
[2, 27],
],
);
assert.equal(map.get(1), 3);
assert.deepEqual(Array.from(map.entries()), [
[1, 3],
[2, 27],
]);
});
it("returns a default value based on a key if it doesn't exist", () => {
let map = new DefaultMap(k => k);
assert.equal(map.get(3), 3);
});
it("sets a default value based on a key if it doesn't exist", () => {
let map = new DefaultMap(k => k);
map.get(3);
assert.deepEqual(Array.from(map.entries()), [[3, 3]]);
});
it('respects undefined/null if it already existed in the map', () => {
let map = new DefaultMap<number, number | void | null>(k => k);
map.set(3, undefined);
assert.equal(map.get(3), undefined);
map.set(4, null);
assert.equal(map.get(4), null);
});
});

View File

@@ -0,0 +1,103 @@
// @flow
import assert from 'assert';
import randomInt from 'random-int';
import PromiseQueue from '../src/PromiseQueue';
import sinon from 'sinon';
describe('PromiseQueue', () => {
it('run() should resolve when all async functions in queue have completed', async () => {
let queue = new PromiseQueue();
let someBooleanToBeChanged = false;
queue.add(() =>
Promise.resolve().then(() => {
someBooleanToBeChanged = true;
}),
);
await queue.run();
assert(someBooleanToBeChanged);
});
it('run() should reject if any of the async functions in the queue failed', async () => {
let error = new Error('some failure');
try {
let queue = new PromiseQueue();
queue
.add(() => Promise.reject(error))
.catch(
/* catch this to prevent an unhandled promise rejection*/ () => {},
);
await queue.run();
} catch (e) {
assert.equal(e, error);
}
});
it('.run() should instantly resolve when the queue is empty', async () => {
let queue = new PromiseQueue();
await queue.run();
// no need to assert, test will hang or throw an error if condition fails
});
it(".add() should resolve with the same result when the passed in function's promise resolves", async () => {
let queue = new PromiseQueue();
let promise = queue.add(() => Promise.resolve(42));
await queue.run();
let result = await promise;
assert.equal(result, 42);
});
it(".add() should reject with the same error when the passed in function's promise rejects", async () => {
let queue = new PromiseQueue();
let error = new Error('Oh no!');
let promise = queue.add(() => Promise.reject(error));
await queue.run().catch(() => null);
await promise.then(null, e => assert.equal(e, error));
});
it('constructor() should allow for configuration of max concurrent running functions', async () => {
const maxConcurrent = 5;
const queue = new PromiseQueue({maxConcurrent});
let running = 0;
new Array(100).fill(0).map(() =>
queue.add(async () => {
running++;
assert(queue._numRunning === running);
assert(running <= maxConcurrent);
await Promise.resolve(randomInt(1, 10)); //sleep(randomInt(1, 10));
running--;
}),
);
await queue.run();
});
it('.add() should notify subscribers', async () => {
const queue = new PromiseQueue();
const subscribedFn = sinon.spy();
queue.subscribeToAdd(subscribedFn);
const promise = queue.add(() => Promise.resolve());
await queue.run();
await promise;
assert(subscribedFn.called);
});
it('.subscribeToAdd() should allow unsubscribing', async () => {
const queue = new PromiseQueue();
const subscribedFn = sinon.spy();
const unsubscribe = queue.subscribeToAdd(subscribedFn);
unsubscribe();
const promise = queue.add(() => Promise.resolve());
await queue.run();
await promise;
assert(!subscribedFn.called);
});
});

View File

@@ -0,0 +1,52 @@
// @flow
import assert from 'assert';
import {
objectSortedEntries,
objectSortedEntriesDeep,
setDifference,
} from '../src/collection';
describe('objectSortedEntries', () => {
it('returns a sorted list of key/value tuples', () => {
assert.deepEqual(
objectSortedEntries({foo: 'foo', baz: 'baz', bar: 'bar'}),
[
['bar', 'bar'],
['baz', 'baz'],
['foo', 'foo'],
],
);
});
});
describe('objectSortedEntriesDeep', () => {
it('returns a deeply sorted list of key/value tuples', () => {
assert.deepEqual(
objectSortedEntriesDeep({
foo: 'foo',
baz: ['d', 'c'],
bar: {g: 'g', b: 'b'},
}),
[
[
'bar',
[
['b', 'b'],
['g', 'g'],
],
],
['baz', ['d', 'c']],
['foo', 'foo'],
],
);
});
});
describe('setDifference', () => {
it('returns a setDifference of two sets of T type', () => {
assert.deepEqual(
setDifference(new Set([1, 2, 3]), new Set([3, 4, 5])),
new Set([1, 2, 4, 5]),
);
});
});

View File

@@ -0,0 +1,98 @@
// @flow strict-local
import assert from 'assert';
import {loadConfig} from '../src/config';
import {inputFS as fs} from '@parcel/test-utils';
import path from 'path';
describe('loadConfig', () => {
it('load config with json', async () => {
assert.deepEqual(
(
await loadConfig(
fs,
path.join(__dirname, './input/config/config.json'),
['config.json'],
path.join(__dirname, './input/config/'),
)
)?.config,
{
hoge: 'fuga',
},
);
});
it('should throw error with empty string json', async () => {
// $FlowFixMe[prop-missing]
await assert.rejects(async () => {
await loadConfig(
fs,
path.join(__dirname, './input/config/empty.json'),
['empty.json'],
path.join(__dirname, './input/config/'),
);
});
});
it('should load with empty string config toml', async () => {
assert.deepEqual(
(
await loadConfig(
fs,
path.join(__dirname, './input/config/empty.toml'),
['empty.toml'],
path.join(__dirname, './input/config/'),
)
)?.config,
{},
);
});
it('should load with js', async () => {
assert.deepEqual(
(
await loadConfig(
fs,
path.join(__dirname, './input/config/config.js'),
['config.js'],
path.join(__dirname, './input/config/'),
)
)?.config,
{
hoge: 'fuga',
},
);
});
it('should load with cjs', async () => {
assert.deepEqual(
(
await loadConfig(
fs,
path.join(__dirname, './input/config/config.cjs'),
['config.cjs'],
path.join(__dirname, './input/config/'),
)
)?.config,
{
hoge: 'fuga',
},
);
});
it('should load without an extension as json', async () => {
assert.deepEqual(
(
await loadConfig(
fs,
path.join(__dirname, './input/config/.testrc'),
['.testrc'],
path.join(__dirname, './input/config/'),
)
)?.config,
{
hoge: 'fuga',
},
);
});
});

View File

@@ -0,0 +1,3 @@
{
"hoge": "fuga"
}

View File

@@ -0,0 +1,3 @@
module.exports = {
hoge: 'fuga',
};

View File

@@ -0,0 +1,3 @@
module.exports = {
hoge: 'fuga',
};

View File

@@ -0,0 +1,3 @@
{
"hoge": "fuga"
}

View File

@@ -0,0 +1 @@
//@ sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiIiwic291cmNlcyI6WyJmb28uanMiLCJiYXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O1VBQ0c7Ozs7Ozs7Ozs7Ozs7O3NCQ0RIO3NCQUNBIn0=

View File

@@ -0,0 +1,6 @@
if ((ref$ = options.map) === 'linked' || ref$ === 'debug') {
mapPath = path.basename(outputFilename) + ".map";
result.code += "\n//# sourceMappingURL=" + mapPath + "\n";
} else {
result.code += "\n//# sourceMappingURL=data:application/json;base64," + bufferFrom(result.map.toString()).toString('base64') + "\n";
}

View File

@@ -0,0 +1,2 @@
function hello(){var l="Hello",o="world";console.log(l+" "+o+"!")}hello();
//# sourceMappingURL=file://referenced-min.js.map

View File

@@ -0,0 +1,6 @@
{
"version":3,
"sources":["./referenced.js"],
"names":["hello","l","o","console","log"],
"mappings":"AAAA,SAASA,QACP,IAAIC,EAAI,QACNC,EAAI,QACNC,QAAQC,IAAIH,EAAI,IAAMC,EAAI,KAE5BF"
}

View File

@@ -0,0 +1,2 @@
function hello(){var l="Hello",o="world";console.log(l+" "+o+"!")}hello();
//# sourceMappingURL=source-root.js.map

View File

@@ -0,0 +1,7 @@
{
"version":3,
"sourceRoot": "../",
"sources":["./source.js"],
"names":["hello","l","o","console","log"],
"mappings":"AAAA,SAASA,QACP,IAAIC,EAAI,QACNC,EAAI,QACNC,QAAQC,IAAIH,EAAI,IAAMC,EAAI,KAE5BF"
}

View File

@@ -0,0 +1,33 @@
// @flow
import assert from 'assert';
import objectHash from '../src/objectHash';
describe('objectHash', () => {
it('calculates the same hash for two different but deep equal objects', () => {
const obj1 = {
foo: {foo: 'foo', baz: ['foo', 'baz', 'bar'], bar: 'bar'},
baz: 'baz',
bar: 'bar',
};
const obj2 = {
foo: {foo: 'foo', baz: ['foo', 'baz', 'bar'], bar: 'bar'},
baz: 'baz',
bar: 'bar',
};
assert.equal(objectHash(obj1), objectHash(obj2));
});
it('calculates a unique hash for two deep equal objects', () => {
const obj1 = {
baz: 'baz',
bar: 'ba',
};
const obj2 = {
baz: 'baz',
bar: 'bar',
};
assert.notEqual(objectHash(obj1), objectHash(obj2));
});
});

View File

@@ -0,0 +1,17 @@
// @flow
import assert from 'assert';
import prettifyTime from '../src/prettifyTime';
describe('prettifyTime', () => {
it('should format numbers less than 1000 as ms', () => {
assert.equal(prettifyTime(888), '888ms');
assert.equal(prettifyTime(50), '50ms');
assert.equal(prettifyTime(0), '0ms');
});
it('should format numbers greater than 1000 as s with 2 fractional digits', () => {
assert.equal(prettifyTime(4000), '4.00s');
assert.equal(prettifyTime(90000), '90.00s');
assert.equal(prettifyTime(45678), '45.68s');
});
});

View File

@@ -0,0 +1,268 @@
// @flow strict-local
import type {NamedBundle, Dependency} from '@parcel/types';
import assert from 'assert';
import {getURLReplacement} from '../src/replaceBundleReferences';
describe('replace bundle references', () => {
it('Query params and named pipeline, relative', () => {
// $FlowFixMe
let fromBundle: NamedBundle = {
filePath: '/user/dist/reformat.html',
name: 'reformat.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let toBundle: NamedBundle = {
filePath:
'/user/dist/image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
name: 'image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let dependency: Dependency = {
id: '074b36596e3147e900a8ad17ceb5c90b',
specifier: 'url:./image.jpg?as=webp',
specifierType: 'esm',
};
let result = getURLReplacement({
dependency,
fromBundle,
toBundle,
relative: true,
});
assert.equal(
result.to,
'image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
);
assert.equal(result.from, '074b36596e3147e900a8ad17ceb5c90b');
});
it('Query params and named pipeline, absolute', () => {
// $FlowFixMe
let fromBundle: NamedBundle = {
filePath: '/user/dist/reformat.html',
name: 'reformat.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let toBundle: NamedBundle = {
filePath:
'/user/dist/image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
name: 'image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let dependency: Dependency = {
id: '074b36596e3147e900a8ad17ceb5c90b',
specifier: 'url:./image.jpg?as=webp',
specifierType: 'esm',
};
let result = getURLReplacement({
dependency,
fromBundle,
toBundle,
relative: false,
});
assert.equal(
result.to,
'/image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
);
assert.equal(result.from, '074b36596e3147e900a8ad17ceb5c90b');
});
it('Custom Public URL', () => {
// $FlowFixMe
let fromBundle: NamedBundle = {
filePath: '/user/dist/reformat.html',
name: 'reformat.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: 'https://test.com/static',
},
};
// $FlowFixMe
let toBundle: NamedBundle = {
filePath:
'/user/dist/image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
name: 'image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: 'https://test.com/static',
},
};
// $FlowFixMe
let dependency: Dependency = {
id: '074b36596e314797845a8ad17ceb5c9b',
specifier: './image.jpg',
specifierType: 'esm',
};
let result = getURLReplacement({
dependency,
fromBundle,
toBundle,
relative: false,
});
assert.equal(
result.to,
'https://test.com/static/image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
);
assert.equal(result.from, '074b36596e314797845a8ad17ceb5c9b');
});
it('Relative with folders in between', () => {
// $FlowFixMe
let fromBundle: NamedBundle = {
filePath: '/user/dist/reformat.html',
name: 'reformat.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: 'https://test.com/static',
},
};
// $FlowFixMe
let toBundle: NamedBundle = {
filePath:
'/user/dist/assets/image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
name: 'image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
// $FlowFixMe
target: {
distDir: '/user/dist/assets',
publicUrl: 'https://test.com/static',
},
};
// $FlowFixMe
let dependency: Dependency = {
id: '074b36596e3147e900a8ad17ceb5c90b',
specifier: 'url:./image.jpg?as=webp',
specifierType: 'esm',
};
let result = getURLReplacement({
dependency,
fromBundle,
toBundle,
relative: true,
});
assert.equal(
result.to,
'assets/image.HASH_REF_87f9d66c16c2216ccc7e5664cf089305.webp',
);
assert.equal(result.from, '074b36596e3147e900a8ad17ceb5c90b');
});
it('should work with bundle names with colons, relative', () => {
// $FlowFixMe
let fromBundle: NamedBundle = {
filePath: '/user/dist/reformat.html',
name: 'reformat.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let toBundle: NamedBundle = {
filePath: '/user/dist/a:b:c.html',
name: 'a:b:c.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let dependency: Dependency = {
id: '074b36596e3147e900a8ad17ceb5c90b',
specifier: './a:b:c.html',
specifierType: 'esm',
};
let result = getURLReplacement({
dependency,
fromBundle,
toBundle,
relative: true,
});
assert.equal(result.to, './a:b:c.html');
});
it('should work with bundle names with colons, absolute', () => {
// $FlowFixMe
let fromBundle: NamedBundle = {
filePath: '/user/dist/reformat.html',
name: 'reformat.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let toBundle: NamedBundle = {
filePath: '/user/dist/a:b:c.html',
name: 'a:b:c.html',
// $FlowFixMe
target: {
distDir: '/user/dist',
publicUrl: '/',
},
};
// $FlowFixMe
let dependency: Dependency = {
id: '074b36596e3147e900a8ad17ceb5c90b',
specifier: './a:b:c.html',
specifierType: 'esm',
};
let result = getURLReplacement({
dependency,
fromBundle,
toBundle,
relative: false,
});
assert.equal(result.to, '/a:b:c.html');
});
});

View File

@@ -0,0 +1,207 @@
import assert from 'assert';
import {
matchSourceMappingURL,
loadSourceMapUrl,
loadSourceMap,
} from '../src/sourcemap';
import {NodeFS} from '@parcel/fs';
import path from 'path';
const fs = new NodeFS();
describe('loadSourceMap', () => {
it('should not match sourceMappingURL when not at the end of the bundle', () => {
// Code example taken from livescript.js (issue #2408 in parcel-bundler)
// This snippet lead to JSAsset.js being mislead and incorrectly trying to
// load (due to false-positive match) sourcemap before fix was introduced
let code = fs.readFileSync(
path.join(__dirname, './input/sourcemap/no-sourcemap.js'),
'utf-8',
);
assert(!matchSourceMappingURL(code));
});
it('should match referenced-min sourceMappingURL when correctly inserted at end of the bundle', () => {
let code = fs.readFileSync(
path.join(__dirname, './input/sourcemap/referenced-min.js'),
'utf-8',
);
assert(!!matchSourceMappingURL(code));
});
it('should match inline sourceMappingURL when correctly inserted at end of the bundle', () => {
// inline source map taken from https://github.com/thlorenz/inline-source-map
let code = fs.readFileSync(
path.join(__dirname, './input/sourcemap/inline.js'),
'utf-8',
);
assert(!!matchSourceMappingURL(code));
});
it('Should be able to load sourcemap data from a url reference', async () => {
let filename = path.join(__dirname, './input/sourcemap/referenced-min.js');
let contents = fs.readFileSync(filename, 'utf-8');
let foundMap = await loadSourceMapUrl(fs, filename, contents);
assert.equal(foundMap.url, 'file://referenced-min.js.map');
assert.equal(
foundMap.filename,
path.join(__dirname, 'input/sourcemap/referenced-min.js.map'),
);
assert.deepEqual(foundMap.map, {
version: 3,
sources: ['./referenced.js'],
names: ['hello', 'l', 'o', 'console', 'log'],
mappings:
'AAAA,SAASA,QACP,IAAIC,EAAI,QACNC,EAAI,QACNC,QAAQC,IAAIH,EAAI,IAAMC,EAAI,KAE5BF',
});
});
it('Should be able to load sourcemap data from an inline url reference', async () => {
let filename = path.join(__dirname, './input/sourcemap/inline.js');
let contents = fs.readFileSync(filename, 'utf-8');
let foundMap = await loadSourceMapUrl(fs, filename, contents);
assert.equal(
foundMap.url,
'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiIiwic291cmNlcyI6WyJmb28uanMiLCJiYXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O1VBQ0c7Ozs7Ozs7Ozs7Ozs7O3NCQ0RIO3NCQUNBIn0=',
);
assert.equal(foundMap.filename, filename);
assert.deepEqual(foundMap.map, {
version: 3,
file: '',
sources: ['foo.js', 'bar.js'],
names: [],
mappings: ';;;;;;;;;UACG;;;;;;;;;;;;;;sBCDH;sBACA',
});
});
it('Should be able to load a SourceMap instance from a file', async () => {
let filename = path.join(__dirname, './input/sourcemap/referenced-min.js');
let contents = fs.readFileSync(filename, 'utf-8');
let map = await loadSourceMap(filename, contents, {
fs,
projectRoot: __dirname,
});
assert(!!map);
let parsedMap = map.getMap();
assert.deepEqual(parsedMap.sources, ['input/sourcemap/referenced.js']);
assert.deepEqual(parsedMap.names, ['hello', 'l', 'o', 'console', 'log']);
assert.deepEqual(parsedMap.mappings, [
{
generated: {line: 1, column: 0},
original: {line: 1, column: 0},
source: 0,
},
{
generated: {line: 1, column: 9},
original: {line: 1, column: 9},
source: 0,
name: 0,
},
{
generated: {line: 1, column: 17},
original: {line: 2, column: 2},
source: 0,
},
{
generated: {line: 1, column: 21},
original: {line: 2, column: 6},
source: 0,
name: 1,
},
{
generated: {line: 1, column: 23},
original: {line: 2, column: 10},
source: 0,
},
{
generated: {line: 1, column: 31},
original: {line: 3, column: 4},
source: 0,
name: 2,
},
{
generated: {line: 1, column: 33},
original: {line: 3, column: 8},
source: 0,
},
{
generated: {line: 1, column: 41},
original: {line: 4, column: 2},
source: 0,
name: 3,
},
{
generated: {line: 1, column: 49},
original: {line: 4, column: 10},
source: 0,
name: 4,
},
{
generated: {line: 1, column: 53},
original: {line: 4, column: 14},
source: 0,
name: 1,
},
{
generated: {line: 1, column: 55},
original: {line: 4, column: 18},
source: 0,
},
{
generated: {line: 1, column: 59},
original: {line: 4, column: 24},
source: 0,
name: 2,
},
{
generated: {line: 1, column: 61},
original: {line: 4, column: 28},
source: 0,
},
{
generated: {line: 1, column: 66},
original: {line: 6, column: 0},
source: 0,
name: 0,
},
]);
});
it('Should remap sources when using sourceRoot', async () => {
let filename = path.join(__dirname, './input/sourcemap/referenced-min.js');
let contents = fs.readFileSync(filename, 'utf-8');
let map = await loadSourceMap(filename, contents, {
fs,
projectRoot: __dirname,
});
assert(!!map);
let parsedMap = map.getMap();
assert.deepEqual(parsedMap.sources, ['input/sourcemap/referenced.js']);
});
it('Should remap sources when using sourceRoot', async () => {
let filename = path.join(__dirname, './input/sourcemap/source-root.js');
let contents = fs.readFileSync(filename, 'utf-8');
let map = await loadSourceMap(filename, contents, {
fs,
projectRoot: __dirname,
});
assert(!!map);
let parsedMap = map.getMap();
assert.deepEqual(parsedMap.sources, ['input/source.js']);
});
});

View File

@@ -0,0 +1,44 @@
// @flow strict-local
import assert from 'assert';
import sinon from 'sinon';
import throttle from '../src/throttle';
describe('throttle', () => {
it("doesn't invoke a function more than once in a given interval", () => {
let spy = sinon.spy();
let throttled = throttle(spy, 100);
throttled(1);
throttled(2);
throttled(3);
assert(spy.calledOnceWithExactly(1));
});
it('calls the underlying function again once the interval has passed', () => {
let time = sinon.useFakeTimers();
let spy = sinon.spy();
let throttled = throttle(spy, 100);
throttled(1);
throttled(2);
throttled(3);
time.tick(100);
throttled(4);
assert.deepEqual(spy.args, [[1], [4]]);
time.restore();
});
it('preserves the `this` when throttled functions are invoked', () => {
let result;
let throttled = throttle(function () {
result = this.bar;
}, 100);
throttled.call({bar: 'baz'});
assert(result === 'baz');
});
});

View File

@@ -0,0 +1,37 @@
// @flow strict-local
import assert from 'assert';
import urlJoin from '../src/urlJoin';
describe('urlJoin', () => {
it('Should join two paths', () => {
let joinedUrl = urlJoin('/', './image.jpeg?test=test');
assert.equal(joinedUrl, '/image.jpeg?test=test');
});
it('Should join two paths with longer publicUrl', () => {
let joinedUrl = urlJoin('/static', './image.jpeg?test=test');
assert.equal(joinedUrl, '/static/image.jpeg?test=test');
});
it('Should join two paths with longer publicUrl', () => {
let joinedUrl = urlJoin('/static', 'image.jpeg?test=test');
assert.equal(joinedUrl, '/static/image.jpeg?test=test');
});
it('Should turn windows path into posix', () => {
let joinedUrl = urlJoin('/static', '.\\image.jpeg?test=test');
assert.equal(joinedUrl, '/static/image.jpeg?test=test');
});
it('should support paths with colons', () => {
let joinedUrl = urlJoin('/static', 'a:b:c.html');
assert.equal(joinedUrl, '/static/a:b:c.html');
joinedUrl = urlJoin('/static', '/a:b:c.html');
assert.equal(joinedUrl, '/static/a:b:c.html');
joinedUrl = urlJoin('/static', './a:b:c.html');
assert.equal(joinedUrl, '/static/a:b:c.html');
});
});