Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/player/src/view/resolver/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe("Async Node Resolution", () => {
};
});

it("should", () => {
it("should clear the cache for the async node and its parent when it is updated", () => {
const beforeResolveFunction = vi.fn((node: Node.Node | null) => node);

const resolver = new Resolver(simpleViewWithAsync, resolverOptions);
Expand Down Expand Up @@ -106,7 +106,7 @@ describe("Async Node Resolution", () => {
);
});

it("should also", () => {
it("should clear the cache for anything with a matching async node in its resolved list on update", () => {
const beforeResolveFunction = vi.fn((node: Node.Node | null) => {
// Add asyncNodesResolved to view to test tracking and invalidation of just the view.
if (node?.type === NodeType.View) {
Expand Down
194 changes: 192 additions & 2 deletions plugins/async-node/core/src/__tests__/createAsyncTransform.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect, beforeEach } from "vitest";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { createAsyncTransform } from "..";
import { Builder, NodeType, Node } from "@player-ui/player";

Expand Down Expand Up @@ -128,6 +128,196 @@ describe("createAsyncTransform", () => {
});
});

describe("getNestedAsset - different node types", () => {
it("should add the async node to an existing multi node", () => {
const nodeIdFn = vi.fn();
nodeIdFn.mockReturnValue("async-node");

const nestedAssetFn = vi.fn();
const nestedAsset: Node.MultiNode = {
type: NodeType.MultiNode,
values: [
{
type: NodeType.Value,
value: undefined,
children: [
{
path: ["asset"],
value: {
type: NodeType.Asset,
value: {
type: "text",
id: "first-asset",
},
},
},
],
},
{
type: NodeType.Value,
value: undefined,
children: [
{
path: ["asset"],
value: {
type: NodeType.Asset,
value: {
type: "text",
id: "second-asset",
},
},
},
],
},
],
};
nestedAssetFn.mockReturnValue(nestedAsset);

const transform = createAsyncTransform({
transformAssetType: "chat-message",
wrapperAssetType: "collection",
flatten: false,
path: ["array"],
getAsyncNodeId: nodeIdFn,
getNestedAsset: nestedAssetFn,
});

const result = transform(asset, {} as any, {} as any);

expect(result).toStrictEqual({
type: NodeType.Asset,
children: [
{
path: ["array"],
value: {
type: NodeType.MultiNode,
override: true,
parent: expect.anything(),
values: [
{
parent: expect.anything(),
type: NodeType.Value,
value: undefined,
children: [
{
path: ["asset"],
value: {
type: NodeType.Asset,
value: {
type: "text",
id: "first-asset",
},
},
},
],
},
{
parent: expect.anything(),
type: NodeType.Value,
value: undefined,
children: [
{
path: ["asset"],
value: {
type: NodeType.Asset,
value: {
type: "text",
id: "second-asset",
},
},
},
],
},
{
parent: expect.anything(),
type: NodeType.Async,
flatten: false,
onValueReceived: undefined,
id: "async-node",
value: {
type: NodeType.Value,
value: {
id: "async-node",
},
},
},
],
},
},
],
value: {
id: "collection-async-node",
type: "collection",
},
});
});

it("should default to adding the node as-is", () => {
const nodeIdFn = vi.fn();
nodeIdFn.mockReturnValue("async-node");

const nestedAssetFn = vi.fn();
const nestedAsset: Node.Value = {
type: NodeType.Value,
value: {
prop: "value",
},
};
nestedAssetFn.mockReturnValue(nestedAsset);

const transform = createAsyncTransform({
transformAssetType: "chat-message",
wrapperAssetType: "collection",
flatten: false,
path: ["array"],
getAsyncNodeId: nodeIdFn,
getNestedAsset: nestedAssetFn,
});

const result = transform(asset, {} as any, {} as any);

expect(result).toStrictEqual({
type: NodeType.Asset,
children: [
{
path: ["array"],
value: {
type: NodeType.MultiNode,
override: true,
parent: expect.anything(),
values: [
{
parent: expect.anything(),
type: NodeType.Value,
value: {
prop: "value",
},
},
{
parent: expect.anything(),
type: NodeType.Async,
flatten: false,
onValueReceived: undefined,
id: "async-node",
value: {
type: NodeType.Value,
value: {
id: "async-node",
},
},
},
],
},
},
],
value: {
id: "collection-async-node",
type: "collection",
},
});
});
});

describe("onValueReceived callback setup", () => {
let transformedAsset: Node.Node;
let onValueReceivedFuncion: ((node: Node.Node) => Node.Node) | undefined;
Expand Down Expand Up @@ -188,7 +378,7 @@ describe("createAsyncTransform", () => {
expect(onValueReceivedFuncion).toBeDefined();
});

it("should add an onValueReceivedFunction that transforms the result into a chained asset on the same type into a multi-node", () => {
it("should use traverseAndReplace as the onValueReceived callback", () => {
const result = onValueReceivedFuncion?.(asset);
expect(result).toStrictEqual({
override: true,
Expand Down
18 changes: 14 additions & 4 deletions plugins/async-node/core/src/createAsyncTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import {
Node,
NodeType,
} from "@player-ui/player";
import { extractNodeFromPath, traverseAndReplace, unwrapAsset } from "./utils";
import {
extractNodeFromPath,
requiresAssetWrapper,
traverseAndReplace,
unwrapAsset,
} from "./utils";

export type AsyncTransformOptions = {
/** Whether or not to flatten the results into its container. Defaults to true */
Expand Down Expand Up @@ -69,10 +74,15 @@ export const createAsyncTransform = (
const asyncNode = Builder.asyncNode(id, flatten, replaceFunction);

let multiNode: Node.MultiNode | undefined;

if (asset) {
const assetNode = Builder.assetWrapper(asset);
multiNode = Builder.multiNode(assetNode, asyncNode);
if (requiresAssetWrapper(asset)) {
const assetWrappedNode = Builder.assetWrapper(asset);
multiNode = Builder.multiNode(assetWrappedNode, asyncNode);
} else if (asset.type === NodeType.MultiNode) {
multiNode = Builder.multiNode(...(asset.values as any[]), asyncNode);
} else {
multiNode = Builder.multiNode(asset as any, asyncNode);
}
} else {
multiNode = Builder.multiNode(asyncNode);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { NodeType, Node } from "@player-ui/player";
import { describe, expect, it } from "vitest";
import { requiresAssetWrapper } from "../requiresAssetWrapper";

describe("requiresAssetWrapper", () => {
it("should return true for asset nodes", () => {
const node: Node.Asset = {
type: NodeType.Asset,
value: {
type: "text",
id: "id",
},
};

const result = requiresAssetWrapper(node);

expect(result).toBe(true);
});

it("should return true for applicability nodes containing asset nodes", () => {
const node: Node.Applicability = {
type: NodeType.Applicability,
expression: "",
value: {
type: NodeType.Asset,
value: {
type: "text",
id: "id",
},
},
};

const result = requiresAssetWrapper(node);

expect(result).toBe(true);
});

it("should return false for non-asset or non-applicability nodes", () => {
const node: Node.Value = {
type: NodeType.Value,
value: {},
};

const result = requiresAssetWrapper(node);

expect(result).toBe(false);
});

it("should return false for applicability nodes that do not contain an asset node", () => {
const node: Node.Applicability = {
type: NodeType.Applicability,
expression: "",
value: {
type: NodeType.Value,
value: {},
},
};

const result = requiresAssetWrapper(node);

expect(result).toBe(false);
});
});
1 change: 1 addition & 0 deletions plugins/async-node/core/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./extractNodeFromPath";
export * from "./traverseAndReplace";
export * from "./unwrapAsset";
export * from "./requiresAssetWrapper";
14 changes: 14 additions & 0 deletions plugins/async-node/core/src/utils/requiresAssetWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { NodeType } from "@player-ui/player";
import type { Node } from "@player-ui/player";

export const requiresAssetWrapper = (node: Node.Node): boolean => {
if (node.type === NodeType.Asset) {
return true;
}

if (node.type !== NodeType.Applicability) {
return false;
}

return node.value.type === NodeType.Asset;
};