Skip to content

Commit

Permalink
fix: ajusta tests de emulador
Browse files Browse the repository at this point in the history
  • Loading branch information
drusco committed Jul 24, 2023
1 parent 110a8a7 commit 04b941c
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 113 deletions.
171 changes: 93 additions & 78 deletions src/Emulator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Emulator from "./Emulator";
const emulator = new Emulator();

describe("Emulator", () => {
describe("methods", () => {
describe("Methods", () => {
describe("use", () => {
it(`Returns a proxy function`, () => {
expect(typeof emulator.use({})).toBe("function");
Expand Down Expand Up @@ -85,6 +85,7 @@ describe("Emulator", () => {
const emulator = new Emulator();
emulator.destroy();
expect(emulator.use).toThrow();
expect(emulator.namespace).toThrow();
});
});

Expand Down Expand Up @@ -149,99 +150,113 @@ describe("Emulator", () => {
});
});

describe("proxy", () => {
describe("get, set, deleteProperty", () => {
it("Can set and get values on a proxy", () => {
const a = emulator.use();
a.set = null;
a.set1 = undefined;
a.set2 = { object: true };
a.set3 = "string";
a.set4 = () => {
console.log("function call");
};
a.set5 = [1, 2, 3];
a.set6 = true;
a.set7 = 12.2;
a.set8 = 45;
a.set9 = new Date();
const deep = { deep: true };
a.set2.sub = false;
a.set2.sub2 = {};
a.set2.sub2.deep = deep;
expect(typeof a.treatAsProxy).toBe("function");
expect(a.set).toStrictEqual(null);
expect(a.set1).toStrictEqual(undefined);
expect(a.set3).toStrictEqual("string");
expect(a.set6).toStrictEqual(true);
expect(a.set7).toStrictEqual(12.2);
expect(a.set8).toStrictEqual(45);
expect(a.set2.sub).toStrictEqual(false);
expect(Emulator.equal(deep, a.set2.sub2.deep)).toStrictEqual(true);
describe("Proxy", () => {
describe("Get, Set and Delete traps", () => {
it("Can get values from a proxy", () => {
const proxy = emulator.use();
const deep = { test: true };

proxy.number = 100;
proxy.null = null;
proxy.boolean = true;
proxy.undefined = undefined;
proxy.object = { test: true };
proxy.array = ["test", emulator.use()];
proxy.string = "test";
proxy.function = () => "test";

proxy.object.boolean = false;
proxy.object.sub = {};
proxy.object.sub.deep = deep;

proxy.toDelete = true;
delete proxy.toDelete;

expect(typeof proxy.unknown).toBe("function");
expect(proxy.number).toBe(100);
expect(proxy.null).toBe(null);
expect(proxy.boolean).toBe(true);
expect(proxy.undefined).toBe(undefined);
expect(typeof proxy.object).toBe("function");
expect(typeof proxy.array).toBe("function");
expect(proxy.string).toBe("test");
expect(proxy.function()).toBe("test");
expect(proxy.object.boolean).toBe(false);
expect(typeof proxy.object.sub).toBe("function");
expect(proxy.object.sub.deep).toBe(emulator.use(deep));
expect(proxy.object.sub.deep.test).toBe(true);
expect(proxy.toDelete).toBe(undefined);
});

it("Will not set a value to the original target", () => {
const a = emulator.use();
const deep = { deep: true, property: undefined };
a.set2 = { object: true };
a.set2.sub = false;
a.set2.sub2 = {};
a.set2.sub2.deep = deep;
a.set2.sub2.deep.property = "hello world";
expect(deep.property).toEqual(undefined);
expect(a.set2.sub2.deep).not.toBe(deep);
expect(Emulator.equal(a.set2.sub2.deep, deep)).toStrictEqual(true);
const deep2 = emulator.use(a.set2.sub2.deep);
// proxy out of proxy is not allowed
expect(deep2).toStrictEqual(a.set2.sub2.deep);
const abc = emulator.use({ property: true });
a.set2.sub2.deep.setProxy = abc;
expect(abc.property).toStrictEqual(a.set2.sub2.deep.setProxy.property);
const proxy = emulator.use();
const deep = { test: true };

proxy.set = { object: true };
proxy.set.sub = {};
proxy.set.sub.deep = deep;
proxy.set.sub.deep.test = false;
emulator.use(deep).test = null;

expect(deep.test).toBe(true);
expect(emulator.use(deep).test).toBe(null);
});

it("Can delete a property from a proxy and its original target", () => {
const a = emulator.use();
const deep = { deep: true, property: undefined };
a.set2 = { object: true };
a.set2.sub = false;
a.set2.sub2 = {};
a.set2.sub2.deep = deep;
delete a.set2.sub2.deep.property;
expect(deep.property).toStrictEqual(undefined);
delete a.set2;
expect(a.set2).toStrictEqual(undefined);
const proxy = emulator.use();
const deep = { deep: true, property: "test" };

proxy.set = { test: true };
proxy.set.sub = {};
proxy.set.sub.deep = deep;

delete proxy.set.sub.deep.property;
delete proxy.set;

expect(deep.property).toBe(undefined);
expect(proxy.set).toBe(undefined);
});
});

describe("construct, apply", () => {
it("can construct an object and return its proxy", () => {
function MyClass() {
this.property = 45;
this.traceable = [55];
this.method = function (param) {
describe("Construct and Apply traps", () => {
it("Can construct an object and return its proxy", () => {
class Test {
property: number = 45;
traceable: number[] = [55];
inner: boolean;
value: any;
method(param: any) {
this.inner = true;
this.value = param;
};
}
}
const Class = emulator.use({});
const instance = new Class(1, [2, 3], null);
instance.property = true;
const TestClassProxy = emulator.use();
const instance = new TestClassProxy(1, [2, 3], null);
const value = instance.call();
expect(instance.property).toStrictEqual(true);
expect(typeof value).toBe("function");
const res = emulator.use(new MyClass());
expect(res.property).toStrictEqual(45);
expect(res.traceable[0]).toStrictEqual(55);
expect(typeof res.unknown).toBe("function");
const test = emulator.use(new Test());
const param = { param: true };
res.method(param);
expect(res.inner).toStrictEqual(true);
expect(Emulator.equal(res.value, param)).toStrictEqual(true);

test.method(param);
instance.property = true;

expect(typeof instance).toBe("function");
expect(typeof value).toBe("function");
expect(typeof test.unknown).toBe("function");
expect(instance.property).toBe(true);
expect(test.property).toBe(45);
expect(test.traceable[0]).toBe(55);
expect(test.inner).toBe(true);
expect(test.value).toBe(emulator.use(param));
});

it("Gets a proxy from an apply trap when target is not a function", () => {
const proxy = emulator.use();
expect(typeof proxy()).toBe("function");
});

it("always get a proxy from an apply trap", () => {
const call = emulator.use();
expect(typeof call()).toBe("function");
it("Gets a value from an apply trap when target is a function", () => {
const proxy = emulator.use((value: number) => value / 2);
expect(proxy(10)).toBe(5);
});
});
});
Expand Down
63 changes: 31 additions & 32 deletions src/Emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,33 +118,33 @@ export default class Emulator
return value;
}

exists(item: EmulatorNS.traceable): boolean {
if (typeof item !== "function") return false;
return proxies.has(item as EmulatorNS.proxy);
exists(value: any): boolean {
if (proxies.has(value) || targets.has(value)) {
return true;
}
return false;
}

getId(item: EmulatorNS.traceable): number {
// Access the internal id of a proxy
if (typeof item !== "function") return;
if (!this.exists(item)) return;
return proxies.get(item as EmulatorNS.proxy).id;
getId(value: EmulatorNS.traceable): number {
if (!this.exists(value)) return -1;
return proxies.get(this.use(value)).id;
}

revoke(proxy: EmulatorNS.proxy): void {
if (!proxies.has(proxy)) return;

const { id, group, dummy, revoke, target } = proxies.get(proxy);
const _private = secret.get(this);
const _group = _private.bindings[group];
const { id, group: namespace, dummy, revoke, target } = proxies.get(proxy);
const data = secret.get(this);
const ns = data.bindings[namespace];

if (_group) {
_group.length--;
if (ns) {
ns.length--;

if (_group.length === 0) {
if (ns.length === 0) {
// delete the group when it is empty
delete _private.bindings[group];
this.emit("deleteGroup", group);
_private.groupCount--;
delete data.bindings[namespace];
this.emit("deleteGroup", namespace);
data.groupCount--;
}
}

Expand All @@ -156,7 +156,7 @@ export default class Emulator

revoke();

_private.activeItems--;
data.activeItems--;

this.emit("revoke", id);
}
Expand All @@ -166,10 +166,19 @@ export default class Emulator
secret.delete(this);
}

isTraceable(value: any): boolean {
if (value === null) return false;
if (this.exists(value)) return false;
if (!(typeof value === "object" || typeof value === "function")) {
return false;
}
return true;
}

async resolve(item: EmulatorNS.traceable): Promise<void> {
// Access the real value of a proxy

if (!this.exists(item) && typeof item != "object") return;
if (!this.exists(item)) return;

return new Promise((resolve): void => {
// 'resolve' event listener must exists
Expand Down Expand Up @@ -237,7 +246,7 @@ const traps = {

if (!sandboxKeys.includes(key)) {
if (targets.has(target) && target[key] !== undefined) {
if (isTraceable(target[key])) {
if (scope.isTraceable(target[key])) {
sandbox[key] = createProxy(scope, target[key], group, origin);
} else {
sandbox[key] = target[key];
Expand All @@ -253,7 +262,7 @@ const traps = {
set(dummy: EmulatorNS.proxy, key: string, value: unknown): boolean {
const item = dummies.get(dummy);
const { scope, group, sandbox } = proxies.get(item);
const traceable = isTraceable(value);
const traceable = scope.isTraceable(value);

const origin: EmulatorNS.origin = {
action: "set",
Expand Down Expand Up @@ -328,16 +337,6 @@ const traps = {
},
};

const isTraceable = ((value: unknown): boolean => {
const isObject = typeof value === "object";
const isFunction = typeof value === "function";
if (!(isObject || isFunction)) return false;
if (value === null) return false;
if (targets.has(value)) return false; // has item linked to it
if (isFunction && proxies.has(value as EmulatorNS.proxy)) return false; // is item
return true;
}) satisfies EmulatorNS.isTraceable;

const createProxy = (
scope: EmulatorNS.EmulatorClass,
target: any,
Expand All @@ -354,7 +353,7 @@ const createProxy = (

const itemId = ++data.itemCount;
const dummy = function () {};
const traceable = isTraceable(target);
const traceable = scope.isTraceable(target);
const { proxy, revoke } = Proxy.revocable(dummy, traps);
const group = bindings[namespace];

Expand Down
6 changes: 3 additions & 3 deletions src/types/Emulator.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { EventEmitter } from "events";

declare namespace Emulator {
interface EmulatorClass extends EventEmitter {}

type isTraceable = (value: unknown) => boolean;
interface EmulatorClass extends EventEmitter {
isTraceable(value: any): boolean;
}

interface options {
[x: string]: unknown;
Expand Down

0 comments on commit 04b941c

Please sign in to comment.