Skip to content

Commit

Permalink
improved browser testSetup: refresh possible
Browse files Browse the repository at this point in the history
  • Loading branch information
maximilianMairinger committed Feb 20, 2024
1 parent 09a671d commit a44ca55
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 131 deletions.
11 changes: 11 additions & 0 deletions test2/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ test.describe("Reflection", () => {
expect(val2.get()).toBe(2)
})

test("refresh retention", () => {
const val = josmLocalStorageReflection("testKey", 2);

expect(val.get()).toBe(2)
val.set(3)
expect(val.get()).toBe(3)
}, () => {
const val2 = josmLocalStorageReflection("testKey", 2);
expect(val2.get()).toBe(3)
})

test("dependent on key", () => {
const val = josmLocalStorageReflection("testKey", 2);

Expand Down
291 changes: 160 additions & 131 deletions test2/testSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,178 +24,207 @@ const toString = (func: Function) => `(${func.toString()})()`

declare var window: any;

export function test(name: string, f: () => (void | Promise<void>)) {
export function test(name: string, ...functions: (() => (void | Promise<void>))[]) {

return playwright.test(name, async ({ page }) => {
await page.goto("/")

// declare proxies for the most important functions. Mainly expect and console
await page.evaluate(() => {


// ported from "circ-clone"
const clone = (() => {
let known: WeakMap<any, any>
return function cloneKeys<Ob extends unknown>(ob: Ob): Ob {
known = new WeakMap()
return cloneKeysRec(ob)
}
function cloneKeysRec(ob: unknown) {
if (typeof ob === "object" && ob !== null) {
if (known.has(ob)) return known.get(ob)
const cloned = new (ob instanceof Array ? Array : Object)
known.set(ob, cloned)
for (const key of Object.keys(ob)) if (cloned[key] === undefined) cloned[key] = cloneKeysRec(ob[key])
// prototype poisoning protection >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return cloned
const allCalls = []
for (let i = 0; i < functions.length; i++) {
const f = functions[i]




// declare proxies for the most important functions. Mainly expect and console
await page.evaluate(() => {

// ported from "circ-clone"
const clone = (() => {
let known: WeakMap<any, any>
return function cloneKeys<Ob extends unknown>(ob: Ob): Ob {
known = new WeakMap()
return cloneKeysRec(ob)
}
else return ob
}
})()
function cloneKeysRec(ob: unknown) {
if (typeof ob === "object" && ob !== null) {
if (known.has(ob)) return known.get(ob)
const cloned = new (ob instanceof Array ? Array : Object)
known.set(ob, cloned)
for (const key of Object.keys(ob)) if (cloned[key] === undefined) cloned[key] = cloneKeysRec(ob[key])
// prototype poisoning protection >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return cloned
}
else return ob
}
})()



window.console = new Proxy(window.console, {
get: (target, prop) => {
return (...args) => {
window.___ProxyCalls.push({
type: "console",
prop: {
method: prop,
args
}
})
return target[prop](...args)
window.console = new Proxy(window.console, {
get: (target, prop) => {
return (...args) => {
window.___ProxyCalls.push({
type: "console",
prop: {
method: prop,
args
}
})
return target[prop](...args)
}
}
}
})
})





class SpecialProp {
constructor(public value: any) {}
}
class SpecialProp {
constructor(public value: any) {}
}


if (window.expect !== undefined) throw new Error('expect is already defined');
window.___ProxyCalls = []
window.___ProxyCallsProms = []
let count = 1
window.expect = new Proxy((testValue: any) => {
let not = false
const prox = new Proxy({}, {
get: (target, prop) => {
if (prop !== "not") {
return (expectedValue) => {
not = false
const specialProp = expectedValue instanceof SpecialProp
const testIsFunc = testValue instanceof Function
let testThrown = false
let testThrowVal: any
let testVal: any
try {
testVal = testIsFunc ? testValue() : testValue
}
catch(e) {
testThrown = true
testThrowVal = e
}
if (testIsFunc && testVal instanceof Promise) {
const p = (async () => {
try {
testVal = await testVal
}
catch(e) {
testThrown = true
testThrowVal = e
}
pushData()
})()
window.___ProxyCallsProms.push(p.finally())
return p
}
else pushData()
function pushData() {
window.___ProxyCalls.push({
type: "expect",
prop: {
count,
method: prop,
not,
testValue: {
isFunc: testIsFunc,
thrown: testThrown,
value: clone(testThrown ? testThrowVal : testVal)
},
expectedValue: {
specialProp,
value: clone(specialProp ? expectedValue.value : expectedValue)
if (window.expect !== undefined) throw new Error('expect is already defined');
window.___ProxyCalls = []
window.___ProxyCallsProms = []
let count = 1
window.expect = new Proxy((testValue: any) => {
let not = false
const prox = new Proxy({}, {
get: (target, prop) => {
if (prop !== "not") {
return (expectedValue) => {
not = false
const specialProp = expectedValue instanceof SpecialProp
const testIsFunc = testValue instanceof Function
let testThrown = false
let testThrowVal: any
let testVal: any
try {
testVal = testIsFunc ? testValue() : testValue
}
catch(e) {
testThrown = true
testThrowVal = e
}
if (testIsFunc && testVal instanceof Promise) {
const p = (async () => {
try {
testVal = await testVal
}
catch(e) {
testThrown = true
testThrowVal = e
}
pushData()
})()
window.___ProxyCallsProms.push(p.finally())
return p
}
else pushData()
function pushData() {
window.___ProxyCalls.push({
type: "expect",
prop: {
count,
method: prop,
not,
testValue: {
isFunc: testIsFunc,
thrown: testThrown,
value: clone(testThrown ? testThrowVal : testVal)
},
expectedValue: {
specialProp,
value: clone(specialProp ? expectedValue.value : expectedValue)
}
}
}
})
count++
})
count++
}
}
}
else {
not = !not
return prox
}
},
});
return prox
}, {
get(target, prop) {
return (...values) => {
return new SpecialProp({prop, values})
}
else {
not = !not
return prox
}
},
});
return prox
}, {
get(target, prop) {
return (...values) => {
return new SpecialProp({prop, values})
}
}
})




})







// handle imports
const userFuncStr = toString(f)
const myUUID = uuid(slugify(name))
const dir = path.resolve(`test2/tmp/${myUUID}-${i}.js`)
await fs.mkdir(path.dirname(dir), {recursive: true})
const importsStrs = injectImports.map(({import: imp, from}) => `import { ${imp.join(", ")} } from "${!from.startsWith("./") ? from : path.join("../../", from)}"`)

await fs.writeFile(dir, importsStrs.join(";\n") + ";\n\n" + userFuncStr, "utf-8")

// bundle user test
const res = await esbuild.build({
entryPoints: [dir],
bundle: true,
write: false
})

await fs.unlink(dir)

const bundled = res.outputFiles[0].text

// run user test
await page.evaluate(bundled);



})


// handle imports
const userFuncStr = toString(f)
const myUUID = uuid(slugify(name))
const dir = path.resolve(`test2/tmp/${myUUID}.js`)
await fs.mkdir(path.dirname(dir), {recursive: true})
const importsStrs = injectImports.map(({import: imp, from}) => `import { ${imp.join(", ")} } from "${!from.startsWith("./") ? from : path.join("../../", from)}"`)
// resolve proxies for the most important functions. Mainly expect and console
const calls = await page.evaluate(async () => {
await Promise.all(window.___ProxyCallsProms)
return window.___ProxyCalls
})

allCalls.push(...calls)

if (i < functions.length - 1) await page.reload({ waitUntil: "load" })
}

await fs.writeFile(dir, importsStrs.join(";\n") + ";\n\n" + userFuncStr, "utf-8")


// bundle user test
const res = await esbuild.build({
entryPoints: [dir],
bundle: true,
write: false
})

// await fs.writeFile(dir + "-bundled.js", res.outputFiles[0].text, "utf-8")




await fs.unlink(dir)

const bundled = res.outputFiles[0].text


// run user test
await page.evaluate(bundled);


// resolve proxies for the most important functions. Mainly expect and console
const calls = await page.evaluate(async () => {
await Promise.all(window.___ProxyCallsProms)
return window.___ProxyCalls
})


for (const { type, prop } of calls) {
for (const { type, prop } of allCalls) {
if (type === "expect") {
const { method, not, testValue, expectedValue, count } = prop
let p = customExpect(!testValue.isFunc ? testValue.value : testValue.thrown ? () => {throw testValue.value} : () => testValue.value) as any
Expand Down

0 comments on commit a44ca55

Please sign in to comment.