Compare commits

..

3 Commits

Author SHA1 Message Date
Aaron Bohy b11a440625 issue render await 2023-07-06 21:13:33 +02:00
Julien Carion (juca) 8b1dc4c43d [FIX] devtools: fix/imp env display
This commit first fixes how object prototype are detected so that it
won't stop as soon as the constructor name of the object is "Object".
This allows displaying every single prototype encountered and closes
https://github.com/odoo/owl/issues/1467.

This commit also improves how the env of a component is displayed by
expanding its chain of prototypes by default while keeping its keys lit
as long as it is their first occurence in the chain.
2023-07-05 11:27:42 +02:00
Julien Carion (juca) c105c6da38 [FIX] devtools: fix symbols handling and display
This commit fixes the three following issues:
- Symbols could never appear in shortened display of objects
- Objects which contained only symbols as keys would be considered to be
  empty and therefore not expandable
- Symbol value edition would create a new property on the object with
  the stringified symbol as key instead of updating its value

closes https://github.com/odoo/owl/issues/1464
2023-06-29 14:15:44 +02:00
4 changed files with 104 additions and 18 deletions
+42
View File
@@ -504,6 +504,48 @@ test("update a sub-component twice in the same frame", async () => {
]).toBeLogged();
});
test.only("abcde", async () => {
class Parent extends Component {
static template = xml`<span t-esc="state.x"/>`;
state = useState({ x: 1 });
setup() {
useLogLifecycle();
}
async _doIt() {
await Promise.resolve();
this.state.x++;
}
async doIt() {
await this._doIt();
this.state.x++;
}
}
const parent = await mount(Parent, fixture);
expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
"Parent:rendered",
"Parent:mounted",
]).toBeLogged();
expect(fixture.innerHTML).toBe("<span>1</span>");
parent.doIt();
await nextTick();
expect([
"Parent:willRender",
"Parent:rendered",
"Parent:willRender",
"Parent:rendered",
"Parent:willPatch",
"Parent:patched",
]).toBeLogged();
expect(fixture.innerHTML).toBe("<span>3</span>");
});
test("update a sub-component twice in the same frame, 2", async () => {
class ChildA extends Component {
static template = xml`<span><t t-esc="val()"/></span>`;
@@ -47,7 +47,7 @@ export class ObjectTreeElement extends Component {
classFor(object) {
// Prototype items will be dyed down to appear less important
if (object.path.some((item) => item?.type === "prototype")) {
if (object.path.some((item) => item?.type === "prototype") && !object.keepLit) {
return "attenuate";
}
// Same for subscription items which are not present in the keys while the keys will be bold
+32 -4
View File
@@ -102,7 +102,7 @@ export const store = reactive({
if (IS_FIREFOX) {
await evalInWindow("window.$0 = $0;", this.activeFrame);
}
const [apps, component] = await evalFunctionInWindow(
const [apps, details] = await evalFunctionInWindow(
"getComponentsTree",
fromOld && this.activeComponent
? [this.activeComponent.path, this.apps, this.activeComponent]
@@ -113,7 +113,8 @@ export const store = reactive({
if (!fromOld && this.settings.expandByDefault) {
this.apps.forEach((tree) => expandNodes(tree, true));
}
this.activeComponent = component;
keepEnvLit(details);
this.activeComponent = details;
},
// Select a component by retrieving its details from the page based on its path
@@ -150,9 +151,11 @@ export const store = reactive({
[component.path],
this.activeFrame
);
this.activeComponent = details;
if (!this.activeComponent) {
if (!details) {
await this.loadComponentsTree(false);
} else {
keepEnvLit(details);
this.activeComponent = details;
}
if (this.page !== "ComponentsTab") {
this.switchTab("ComponentsTab");
@@ -887,6 +890,31 @@ function expandNodes(node, blacklist = false) {
}
}
// This function transforms the env part of the details such that all env keys are not
// greyed out in the UI at their first occurence
function keepEnvLit(details) {
let alreadyMet = new Set();
for (let i = 0; i < details.env.children.length; i++) {
if (i < details.env.children.length - 1) {
alreadyMet.add(details.env.children[i].name);
} else {
let lastElement = details.env.children[i];
while (lastElement.children.at(-1).name === "[[Prototype]]") {
for (const [index, child] of lastElement.children.entries()) {
if (index < lastElement.children.length - 1) {
if (!alreadyMet.has(child.name)) {
child.keepLit = true;
alreadyMet.add(child.name);
}
} else {
lastElement = child;
}
}
}
}
}
}
// Fold the node given in entry and all of its children
function foldNodes(node) {
node.toggled = false;
@@ -117,6 +117,15 @@
length += element.length;
result.push(element);
}
for (const key of Object.getOwnPropertySymbols(obj)) {
if (length > 25) {
result.push("...");
break;
}
const element = key.toString() + ": " + this.serializeItem(obj[key]);
length += element.length;
result.push(element);
}
return "{" + result.join(", ") + "}";
},
map(obj) {
@@ -739,6 +748,9 @@
child.contentType = "object";
child.content = this.serializer.serializeItem(Object.getPrototypeOf(parentObj), true);
child.hasChildren = true;
if (!oldTree && type === "env") {
child.toggled = true;
}
break;
case "set entries":
case "map entries":
@@ -815,7 +827,8 @@
break;
case obj instanceof Object:
child.contentType = "object";
child.hasChildren = Object.keys(obj).length > 0;
child.hasChildren =
Object.keys(obj).length || Object.getOwnPropertySymbols(obj).length;
break;
default:
child.contentType = typeof obj;
@@ -880,7 +893,7 @@
const children = [];
depth = depth + 1;
let obj = this.getObjectProperty(path);
let oldBranch = this.getObjectInOldTree(oldTree, path, objType);
let oldBranch = oldTree && this.getObjectInOldTree(oldTree, path, objType);
if (!obj) {
return [];
}
@@ -895,7 +908,7 @@
depth,
objType,
path,
oldBranch.children[0],
oldBranch?.children[0],
oldTree
);
children.push(mapKey);
@@ -905,7 +918,7 @@
depth,
objType,
path,
oldBranch.children[1],
oldBranch?.children[1],
oldTree
);
children.push(mapValue);
@@ -916,7 +929,7 @@
depth,
objType,
path,
oldBranch.children[0],
oldBranch?.children[0],
oldTree
);
children.push(setValue);
@@ -937,7 +950,7 @@
depth,
objType,
path,
oldBranch.children[index],
oldBranch?.children[index],
oldTree
);
if (child) {
@@ -952,7 +965,7 @@
depth,
objType,
path,
oldBranch.children[index],
oldBranch?.children[index],
oldTree
);
if (child) {
@@ -969,7 +982,7 @@
depth,
objType,
path,
oldBranch.children[index],
oldBranch?.children[index],
oldTree
);
if (entries) {
@@ -983,7 +996,7 @@
depth,
objType,
path,
oldBranch.children[index],
oldBranch?.children[index],
oldTree
);
if (child) {
@@ -1018,7 +1031,7 @@
depth,
objType,
path,
oldBranch.children[index],
oldBranch?.children[index],
oldTree
);
if (child) children.push(child);
@@ -1051,14 +1064,14 @@
});
proto = Object.getPrototypeOf(proto);
}
if (!(obj.constructor.name === "Object")) {
if (obj.__proto__) {
prototype = this.serializeObjectChild(
obj,
{ type: "prototype", childIndex: children.length },
depth,
objType,
path,
oldBranch.children.at(-1),
oldBranch?.children.at(-1),
oldTree
);
children.push(prototype);
@@ -1433,8 +1446,11 @@
return;
}
}
const key = path.pop().value;
const item = path.pop();
const obj = this.getObjectProperty(path);
const key = item.hasOwnProperty("symbolIndex")
? Object.getOwnPropertySymbols(obj)[item.symbolIndex]
: item.value;
if (!obj) {
return;
}