-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
38d338c
commit 64def71
Showing
9 changed files
with
379 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export const factorify = (factoryLike) => | ||
typeof factoryLike === 'function' ? factoryLike : () => factoryLike; | ||
export const createInjector = ({ services }) => { | ||
const proxy = new Proxy(services, { | ||
get(target, prop, receiver) { | ||
if (!Reflect.has(services, prop)) { | ||
throw new Error(`could not resolve injectable "${prop}"`); | ||
} | ||
const factory = factorify(services[prop]); | ||
return factory(proxy); | ||
}, | ||
}); | ||
|
||
return proxy; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { test } from '@cofn/test-lib/client'; | ||
import { define } from '@cofn/core'; | ||
import { inject, provide } from '../src/index.js'; | ||
import { nextTick } from './utils.js'; | ||
|
||
const debug = document.getElementById('debug'); | ||
const dumb = function* () {}; | ||
|
||
define('test-dumb-child', dumb); | ||
|
||
test('DOM element that registers some injectables has "provider" attribute', async ({ | ||
eq, | ||
}) => { | ||
define( | ||
'test-provider', | ||
provide({ | ||
a: 'a', | ||
})(dumb), | ||
); | ||
|
||
const root = document.createElement('test-provider'); | ||
root.append(document.createElement('test-dumb-child')); | ||
|
||
debug.append(root); | ||
|
||
await nextTick(); | ||
|
||
eq(root.hasAttribute('provider'), true); | ||
}); | ||
test('"inject" injects services which were registered by a parent "provider element" ', async ({ | ||
eq, | ||
}) => { | ||
define( | ||
'test-provider-1', | ||
provide({ | ||
a: 'a', | ||
b: 'b', | ||
})(dumb), | ||
); | ||
define( | ||
'test-consumer', | ||
inject(function* ({ $host, services }) { | ||
$host.a = services.a; | ||
$host.b = services.b; | ||
}), | ||
); | ||
|
||
const root = document.createElement('test-provider-1'); | ||
const anyChild = document.createElement('test-dumb-child'); | ||
const injected = document.createElement('test-consumer'); | ||
|
||
anyChild.appendChild(injected); | ||
root.append(anyChild); | ||
debug.append(root); | ||
|
||
await nextTick(); | ||
|
||
eq(injected.a, 'a'); | ||
eq(injected.b, 'b'); | ||
}); | ||
|
||
test('provider can be a function which has the $host as a parameter', async ({ | ||
eq, | ||
}) => { | ||
define( | ||
'test-provider-fn', | ||
provide(({ $host }) => { | ||
return { | ||
injected: $host.getAttribute('woot'), | ||
}; | ||
})(dumb), | ||
); | ||
define( | ||
'test-consumer-fn', | ||
inject(function* ({ $host, services }) { | ||
$host.injected = services.injected; | ||
}), | ||
); | ||
|
||
const root = document.createElement('test-provider-fn'); | ||
root.setAttribute('woot', 'bar'); | ||
const anyChild = document.createElement('test-dumb-child'); | ||
const injected = document.createElement('test-consumer-fn'); | ||
|
||
anyChild.appendChild(injected); | ||
root.append(anyChild); | ||
debug.append(root); | ||
|
||
await nextTick(); | ||
|
||
eq(injected.injected, 'bar'); | ||
}); | ||
|
||
test(`provider element shadows parent's injectables`, async ({ eq }) => { | ||
define( | ||
'test-provider-root', | ||
provide({ | ||
a: 'a', | ||
b: 'b', | ||
})(dumb), | ||
); | ||
define( | ||
'test-provider-child', | ||
provide({ | ||
a: 'abis', | ||
})(dumb), | ||
); | ||
|
||
define( | ||
'test-consumer-shadowed', | ||
inject(function* ({ $host, services }) { | ||
$host.a = services.a; | ||
$host.b = services.b; | ||
}), | ||
); | ||
|
||
const root = document.createElement('test-provider-root'); | ||
const anyChild = document.createElement('test-provider-child'); | ||
const injected = document.createElement('test-consumer-shadowed'); | ||
|
||
anyChild.appendChild(injected); | ||
root.append(anyChild); | ||
debug.append(root); | ||
|
||
await nextTick(); | ||
|
||
eq(injected.a, 'abis'); | ||
eq(injected.b, 'b'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { test } from '@cofn/test-lib/client'; | ||
import { createInjector } from '../src/injector.js'; | ||
|
||
test('instantiates an injectable, calling the factory', ({ eq }) => { | ||
const { a } = createInjector({ | ||
services: { | ||
a: () => 'a', | ||
}, | ||
}); | ||
|
||
eq(a, 'a'); | ||
}); | ||
|
||
test('instantiates an injectable, when it is a value', ({ eq }) => { | ||
const { a } = createInjector({ | ||
services: { | ||
a: 'a', | ||
}, | ||
}); | ||
|
||
eq(a, 'a'); | ||
}); | ||
|
||
test('everytime the getter is called a new instance is created', ({ | ||
eq, | ||
isNot, | ||
}) => { | ||
const services = createInjector({ | ||
injectables: { | ||
a: () => ({ prop: 'a' }), | ||
}, | ||
}); | ||
const instance1 = services.a; | ||
const { a: instance2 } = services; | ||
eq(instance1, { prop: 'a' }); | ||
eq(instance2, { prop: 'a' }); | ||
isNot(instance2, instance1); | ||
}); | ||
|
||
test('resolves dependency graph, instantiating the transitive dependencies ', ({ | ||
eq, | ||
}) => { | ||
const services = createInjector({ | ||
injectables: { | ||
a: ({ b, c }) => b + '+' + c, | ||
b: () => 'b', | ||
c: ({ d }) => d, | ||
d: 'd', | ||
}, | ||
}); | ||
eq(services.a, 'b+d'); | ||
}); | ||
|
||
test('injection tokens can be symbols', ({ eq }) => { | ||
const aSymbol = Symbol('a'); | ||
const bSymbol = Symbol('b'); | ||
const cSymbol = Symbol('c'); | ||
const dSymbol = Symbol('d'); | ||
|
||
const services = createInjector({ | ||
services: { | ||
[aSymbol]: ({ [bSymbol]: b, [cSymbol]: c }) => b + '+' + c, | ||
[bSymbol]: () => 'b', | ||
[cSymbol]: ({ [dSymbol]: d }) => d, | ||
[dSymbol]: 'd', | ||
}, | ||
}); | ||
eq(services[aSymbol], 'b+d'); | ||
}); | ||
|
||
test(`only instantiates an injectable when required`, ({ eq, notOk, ok }) => { | ||
let aInstantiated = false; | ||
let bInstantiated = false; | ||
let cInstantiated = false; | ||
|
||
const services = createInjector({ | ||
services: { | ||
a: ({ b }) => { | ||
aInstantiated = true; | ||
return b; | ||
}, | ||
b: () => { | ||
bInstantiated = true; | ||
return 'b'; | ||
}, | ||
c: () => { | ||
cInstantiated = true; | ||
return 'c'; | ||
}, | ||
}, | ||
}); | ||
|
||
const { a } = services; | ||
|
||
eq(a, 'b'); | ||
ok(aInstantiated); | ||
ok(bInstantiated); | ||
notOk(cInstantiated); | ||
|
||
const { c } = services; | ||
eq(c, 'c'); | ||
ok(cInstantiated); | ||
}); | ||
|
||
test('gives a friendly message when it can not resolve a dependency', ({ | ||
eq, | ||
fail, | ||
}) => { | ||
const services = createInjector({ | ||
services: { | ||
a: ({ b }) => b, | ||
b: ({ c }) => c, | ||
}, | ||
}); | ||
|
||
try { | ||
const { a } = services; | ||
fail('should not reach that statement'); | ||
} catch (err) { | ||
eq(err.message, 'could not resolve injectable "c"'); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { createServer } from 'vite'; | ||
import { firefox, chromium, webkit } from 'playwright'; | ||
|
||
const PORT = 3002; | ||
const TIMEOUT = 30_000; | ||
|
||
(async () => { | ||
let server, | ||
browsers = []; | ||
const browserList = [firefox, chromium, webkit]; | ||
try { | ||
server = await createServer({ | ||
server: { | ||
port: PORT, | ||
}, | ||
}); | ||
await server.listen(); | ||
|
||
browsers = await Promise.all( | ||
browserList.map((browserApp) => browserApp.launch({ headless: true })), | ||
); | ||
|
||
await Promise.all( | ||
browsers.map((browser) => { | ||
console.log(browser._name); | ||
return new Promise((resolve, reject) => { | ||
let timerId; | ||
Promise.race([ | ||
browser | ||
.newPage() | ||
.then((page) => { | ||
page.on('websocket', (webSocket) => { | ||
webSocket.on('framesent', ({ payload }) => { | ||
const asJson = JSON.parse(payload); | ||
if (asJson?.data?.type === 'STREAM_ENDED') { | ||
clearTimeout(timerId); | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
|
||
return page.goto( | ||
`http://localhost:${PORT}/test/test-suite.html`, | ||
); | ||
}) | ||
.catch(reject), | ||
new Promise((resolve, reject) => { | ||
timerId = setTimeout(() => reject('timeout'), TIMEOUT); | ||
}), | ||
]); | ||
}); | ||
}), | ||
); | ||
} catch (e) { | ||
console.error(e); | ||
process.exitCode = 1; | ||
} finally { | ||
await Promise.all(browsers.map((browser) => browser.close())); | ||
if (server) { | ||
await server.close(); | ||
} | ||
} | ||
})(); |
Oops, something went wrong.