mirror of
https://gitee.com/antv/g6.git
synced 2024-11-30 10:48:24 +08:00
test: add test case for viewport with svg renderer (#5441)
* test: add test case for viewport with svg renderer * chore: rename file * chore: update test case * chore: keep the comment * feat: add createGraphCavas, and use it in main.ts * chore: move viewport.spec.ts to unit * test: use real graph instance instead of mock * chore: remove unused code * chore: fix ci
This commit is contained in:
parent
0f21e8d6fa
commit
a4346ddc2b
@ -1,21 +1,7 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import { createGraph } from '../../mock';
|
||||
import type { AnimationTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElementPosition: AnimationTestCase = async (context) => {
|
||||
const { canvas } = context;
|
||||
|
||||
@ -51,15 +37,14 @@ export const controllerElementPosition: AnimationTestCase = async (context) => {
|
||||
edge: { style: {} },
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
const graph = createGraph(options, canvas);
|
||||
const r = await graph.draw();
|
||||
await r?.finished;
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
// @ts-expect-error context is private.
|
||||
const element = graph.context.element!;
|
||||
|
||||
const renderResult = await elementController.render(elementContext);
|
||||
|
||||
await renderResult?.finished;
|
||||
|
||||
const result = elementController.updateNodeLikePosition(
|
||||
const result = element.updateNodeLikePosition(
|
||||
{
|
||||
'node-1': [250, 100],
|
||||
'node-2': [175, 200],
|
||||
|
@ -1,21 +1,7 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import { createGraph } from '../../mock';
|
||||
import type { AnimationTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElementState: AnimationTestCase = async (context) => {
|
||||
const { canvas } = context;
|
||||
|
||||
@ -71,15 +57,11 @@ export const controllerElementState: AnimationTestCase = async (context) => {
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
const graph = createGraph(options, canvas);
|
||||
const r = await graph.draw();
|
||||
await r?.finished;
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const renderResult = await elementController.render(elementContext);
|
||||
|
||||
await renderResult?.finished;
|
||||
|
||||
elementContext.model.updateData({
|
||||
graph.updateData({
|
||||
nodes: [
|
||||
{ id: 'node-1', style: { states: [] } },
|
||||
{ id: 'node-2', style: { states: ['active'] } },
|
||||
@ -91,8 +73,7 @@ export const controllerElementState: AnimationTestCase = async (context) => {
|
||||
],
|
||||
});
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
const result = await graph.draw();
|
||||
return result;
|
||||
};
|
||||
|
||||
|
@ -1,21 +1,7 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import { createGraph } from '../../mock';
|
||||
import type { AnimationTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElement: AnimationTestCase = async (context) => {
|
||||
const { canvas } = context;
|
||||
|
||||
@ -44,26 +30,21 @@ export const controllerElement: AnimationTestCase = async (context) => {
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
const graph = createGraph(options, canvas);
|
||||
const r = await graph.draw();
|
||||
await r?.finished;
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const renderResult = await elementController.render(elementContext);
|
||||
|
||||
await renderResult?.finished;
|
||||
|
||||
elementContext.model.addNodeData([
|
||||
graph.addNodeData([
|
||||
{ id: 'node-4', style: { x: 50, y: 200, stroke: 'orange' } },
|
||||
{ id: 'node-5', style: { x: 75, y: 150, stroke: 'purple' } },
|
||||
{ id: 'node-6', style: { x: 200, y: 100, stroke: 'cyan' } },
|
||||
]);
|
||||
|
||||
elementContext.model.removeNodeData(['node-1']);
|
||||
graph.removeNodeData(['node-1']);
|
||||
|
||||
elementContext.model.updateNodeData([{ id: 'node-2', style: { x: 200, y: 200, stroke: 'green' } }]);
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
graph.updateNodeData([{ id: 'node-2', style: { x: 200, y: 200, stroke: 'green' } }]);
|
||||
|
||||
const result = await graph.draw();
|
||||
return result;
|
||||
};
|
||||
|
||||
|
@ -1,21 +1,7 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import { createGraph } from '../../mock';
|
||||
import type { StaticTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElementPosition: StaticTestCase = async (context) => {
|
||||
const { canvas, animation } = context;
|
||||
|
||||
@ -55,15 +41,10 @@ export const controllerElementPosition: StaticTestCase = async (context) => {
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
const graph = createGraph(options, canvas);
|
||||
await graph.render();
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
await result?.finished;
|
||||
|
||||
elementController.updateNodeLikePosition(
|
||||
await graph.translateElementTo(
|
||||
{
|
||||
'node-1': [250, 100],
|
||||
'node-2': [175, 200],
|
||||
@ -74,6 +55,4 @@ export const controllerElementPosition: StaticTestCase = async (context) => {
|
||||
},
|
||||
animation,
|
||||
);
|
||||
|
||||
await elementController.render(elementContext);
|
||||
};
|
||||
|
@ -1,22 +1,8 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { idOf } from '../../../src/utils/id';
|
||||
import { Graph } from '../../mock';
|
||||
import { createGraph } from '../../mock';
|
||||
import type { StaticTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElementVisibility: StaticTestCase = async (context) => {
|
||||
const { canvas, animation, toMatchSVGSnapshot, env } = context;
|
||||
|
||||
@ -48,29 +34,18 @@ export const controllerElementVisibility: StaticTestCase = async (context) => {
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
await result?.finished;
|
||||
const graph = createGraph(options, canvas);
|
||||
await graph.render();
|
||||
|
||||
const hide = () =>
|
||||
elementController.setElementsVisibility(
|
||||
['node-3', idOf(options.data!.edges![1]), idOf(options.data!.edges![2])],
|
||||
'hidden',
|
||||
);
|
||||
graph.setElementVisibility(['node-3', idOf(options.data!.edges![1]), idOf(options.data!.edges![2])], 'hidden');
|
||||
const show = () =>
|
||||
elementController.setElementsVisibility(
|
||||
['node-3', idOf(options.data!.edges![1]), idOf(options.data!.edges![2])],
|
||||
'visible',
|
||||
);
|
||||
graph.setElementVisibility(['node-3', idOf(options.data!.edges![1]), idOf(options.data!.edges![2])], 'visible');
|
||||
|
||||
if (env === 'test') {
|
||||
await hide()?.finished;
|
||||
await hide();
|
||||
await toMatchSVGSnapshot?.('hidden');
|
||||
await show()?.finished;
|
||||
await show();
|
||||
}
|
||||
|
||||
controllerElementVisibility.form = [
|
||||
|
@ -1,21 +1,7 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import { createGraph } from '../../mock';
|
||||
import type { StaticTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElementZIndex: StaticTestCase = async (context) => {
|
||||
const { canvas, animation, toMatchSVGSnapshot, env } = context;
|
||||
|
||||
@ -38,17 +24,12 @@ export const controllerElementZIndex: StaticTestCase = async (context) => {
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
const graph = createGraph(options, canvas);
|
||||
await graph.render();
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
await result?.finished;
|
||||
|
||||
const front = () => elementController.setElementZIndex('node-2', 'front');
|
||||
const back = () => elementController.setElementZIndex('node-2', 'back');
|
||||
const to = (zIndex: number) => elementController.setElementZIndex('node-2', zIndex);
|
||||
const front = () => graph.setElementZIndex('node-2', 'front');
|
||||
const back = () => graph.setElementZIndex('node-2', 'back');
|
||||
const to = (zIndex: number) => graph.setElementZIndex('node-2', zIndex);
|
||||
|
||||
if (env === 'test') {
|
||||
front();
|
||||
|
@ -1,21 +1,7 @@
|
||||
import type { G6Spec } from '../../../src';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { Graph } from '../../mock';
|
||||
import { createGraph } from '../../mock';
|
||||
import type { StaticTestCase } from '../types';
|
||||
|
||||
const createContext = (canvas: any, options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
|
||||
export const controllerElement: StaticTestCase = async (context) => {
|
||||
const { canvas, animation } = context;
|
||||
|
||||
@ -46,11 +32,7 @@ export const controllerElement: StaticTestCase = async (context) => {
|
||||
},
|
||||
};
|
||||
|
||||
const elementContext = createContext(canvas, options);
|
||||
const graph = createGraph(options, canvas);
|
||||
|
||||
const elementController = new ElementController(elementContext);
|
||||
|
||||
const result = await elementController.render(elementContext);
|
||||
|
||||
await result?.finished;
|
||||
await graph.render();
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import '../../src/preset';
|
||||
import * as animationCases from '../demo/animation';
|
||||
import { createNodeGCanvas } from './utils/create-node-g-canvas';
|
||||
import { createGraphCanvas } from '../mock/create';
|
||||
import { getCases } from './utils/get-cases';
|
||||
import { sleep } from './utils/sleep';
|
||||
import './utils/use-snapshot-matchers';
|
||||
@ -10,7 +10,7 @@ describe('static', () => {
|
||||
|
||||
for (const [name, testCase] of cases) {
|
||||
it(`[animation]: ${name}`, async () => {
|
||||
const canvas = createNodeGCanvas();
|
||||
const canvas = createGraphCanvas();
|
||||
|
||||
try {
|
||||
const { times = [], preprocess, postprocess } = testCase;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import '../../src/preset';
|
||||
import * as staticCases from '../demo/static/common';
|
||||
import { createNodeGCanvas } from './utils/create-node-g-canvas';
|
||||
import { createGraphCanvas } from '../mock/create';
|
||||
import { getCases } from './utils/get-cases';
|
||||
import { sleep } from './utils/sleep';
|
||||
import './utils/use-snapshot-matchers';
|
||||
@ -10,7 +10,7 @@ describe('static', () => {
|
||||
|
||||
for (const [name, testCase] of cases) {
|
||||
it(`[static]: ${name}`, async () => {
|
||||
const canvas = createNodeGCanvas();
|
||||
const canvas = createGraphCanvas();
|
||||
|
||||
try {
|
||||
const { preprocess, postprocess } = testCase;
|
||||
|
@ -1,29 +0,0 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import { Plugin as DragAndDropPlugin } from '@antv/g-plugin-dragndrop';
|
||||
import { Renderer as SVGRenderer } from '@antv/g-svg';
|
||||
import { Canvas } from '../../../src/runtime/canvas';
|
||||
import { OffscreenCanvasContext } from './offscreen-canvas-context';
|
||||
|
||||
export function createNodeGCanvas(dom?: HTMLDivElement, width = 500, height = 500) {
|
||||
const container = dom || document.createElement('div');
|
||||
container.style.width = `${width}px`;
|
||||
container.style.height = `${height}px`;
|
||||
|
||||
resetEntityCounter();
|
||||
const offscreenNodeCanvas = {
|
||||
getContext: () => context,
|
||||
} as unknown as HTMLCanvasElement;
|
||||
const context = new OffscreenCanvasContext(offscreenNodeCanvas);
|
||||
|
||||
const renderer = new SVGRenderer();
|
||||
renderer.registerPlugin(new DragAndDropPlugin({ dragstartDistanceThreshold: 10 }));
|
||||
return new Canvas({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
renderer: () => new SVGRenderer(),
|
||||
// @ts-expect-error offscreenCanvas is not in the type definition
|
||||
document: container.ownerDocument,
|
||||
offscreenCanvas: offscreenNodeCanvas,
|
||||
});
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
import { Renderer as CanvasRenderer } from '@antv/g-canvas';
|
||||
import { Renderer as SVGRenderer } from '@antv/g-svg';
|
||||
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
|
||||
import '../src/preset';
|
||||
import { Canvas } from '../src/runtime/canvas';
|
||||
import * as animations from './demo/animation';
|
||||
import * as statics from './demo/static';
|
||||
import type { TestCase } from './demo/types';
|
||||
import { createGraphCanvas } from './mock';
|
||||
|
||||
const CASES = {
|
||||
statics,
|
||||
@ -54,32 +51,14 @@ function loadCasesList(select: HTMLSelectElement) {
|
||||
});
|
||||
}
|
||||
|
||||
function onchange(testCase: TestCase, rendererName: string, animation: boolean) {
|
||||
const renderer = getRenderer(rendererName);
|
||||
const canvas = new Canvas({
|
||||
width: 500,
|
||||
height: 500,
|
||||
container: document.getElementById('container')!,
|
||||
renderer,
|
||||
});
|
||||
function onchange(testCase: TestCase, renderer: string, animation: boolean) {
|
||||
const canvas = createGraphCanvas(document.getElementById('container'), 500, 500, renderer);
|
||||
|
||||
return canvas.init().then(async () => {
|
||||
await testCase({ canvas, animation, env: 'dev' });
|
||||
});
|
||||
}
|
||||
|
||||
function getRenderer(rendererName: string) {
|
||||
switch (rendererName) {
|
||||
case 'webgl':
|
||||
return () => new WebGLRenderer();
|
||||
case 'svg':
|
||||
return () => new SVGRenderer();
|
||||
case 'canvas':
|
||||
return () => new CanvasRenderer();
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
document.getElementById('container')?.remove();
|
||||
const container = document.createElement('div');
|
||||
|
76
packages/g6/__tests__/mock/create.ts
Normal file
76
packages/g6/__tests__/mock/create.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import type { IRenderer } from '@antv/g';
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import { Renderer as CanvasRenderer } from '@antv/g-canvas';
|
||||
import { Plugin as DragAndDropPlugin } from '@antv/g-plugin-dragndrop';
|
||||
import { Renderer as SVGRenderer } from '@antv/g-svg';
|
||||
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
|
||||
import type { G6Spec } from '../../src';
|
||||
import { Graph } from '../../src';
|
||||
import { Canvas } from '../../src/runtime/canvas';
|
||||
import { OffscreenCanvasContext } from './offscreen-canvas-context';
|
||||
|
||||
/**
|
||||
* Create a graph with the given options, and use mock Canvas.
|
||||
* @param options - options
|
||||
* @param graphCanvas - canvas
|
||||
* @returns Graph instance
|
||||
*/
|
||||
export function createGraph(options: G6Spec, graphCanvas?: Canvas) {
|
||||
const { width, height } = options;
|
||||
const canvas = graphCanvas || createGraphCanvas(undefined, width, height);
|
||||
|
||||
return new Graph({
|
||||
...options,
|
||||
container: canvas, // Use mock Canvas.
|
||||
});
|
||||
}
|
||||
|
||||
function getRenderer(renderer: string) {
|
||||
switch (renderer) {
|
||||
case 'webgl':
|
||||
return new WebGLRenderer();
|
||||
case 'svg':
|
||||
return new SVGRenderer();
|
||||
case 'canvas':
|
||||
return new CanvasRenderer();
|
||||
default:
|
||||
return new SVGRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create graph canvas with config.
|
||||
* @param dom - dom
|
||||
* @param width - width
|
||||
* @param height - height
|
||||
* @param renderer - render
|
||||
* @returns instance
|
||||
*/
|
||||
export function createGraphCanvas(
|
||||
dom?: null | HTMLElement,
|
||||
width: number = 500,
|
||||
height: number = 500,
|
||||
renderer: string = 'svg',
|
||||
) {
|
||||
const container = dom || document.createElement('div');
|
||||
container.style.width = `${width}px`;
|
||||
container.style.height = `${height}px`;
|
||||
|
||||
resetEntityCounter();
|
||||
const offscreenNodeCanvas = {
|
||||
getContext: () => context,
|
||||
} as unknown as HTMLCanvasElement;
|
||||
const context = new OffscreenCanvasContext(offscreenNodeCanvas);
|
||||
|
||||
const instance = getRenderer(renderer) as any as IRenderer;
|
||||
instance.registerPlugin(new DragAndDropPlugin({ dragstartDistanceThreshold: 10 }));
|
||||
return new Canvas({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
renderer: () => instance,
|
||||
// @ts-expect-error document offscreenCanvas is not in the type definition
|
||||
document: container.ownerDocument,
|
||||
offscreenCanvas: offscreenNodeCanvas,
|
||||
});
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import EventEmitter from '@antv/event-emitter';
|
||||
|
||||
export class Graph extends EventEmitter {}
|
@ -1 +1 @@
|
||||
export * from './graph';
|
||||
export { createGraph, createGraphCanvas } from './create';
|
||||
|
@ -1,35 +1,10 @@
|
||||
import { omit } from '@antv/util';
|
||||
import type { G6Spec } from '../../../src';
|
||||
import * as BUILT_IN_PALETTES from '../../../src/palettes';
|
||||
import '../../../src/preset';
|
||||
import { DataController } from '../../../src/runtime/data';
|
||||
import { ElementController } from '../../../src/runtime/element';
|
||||
import type { RuntimeContext } from '../../../src/runtime/types';
|
||||
import { light as LIGHT_THEME } from '../../../src/themes';
|
||||
import { idOf } from '../../../src/utils/id';
|
||||
import { Graph } from '../../mock';
|
||||
|
||||
class Canvas {
|
||||
init() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
children: unknown[] = [];
|
||||
appendChild(node: unknown) {
|
||||
this.children.push(node);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
const createContext = (options: G6Spec): RuntimeContext => {
|
||||
const model = new DataController();
|
||||
model.setData(options.data || {});
|
||||
return {
|
||||
canvas: new Canvas() as any,
|
||||
graph: new Graph() as any,
|
||||
options,
|
||||
model,
|
||||
};
|
||||
};
|
||||
import { createGraph } from '../../mock';
|
||||
|
||||
describe('ElementController', () => {
|
||||
it('static', async () => {
|
||||
@ -83,17 +58,16 @@ describe('ElementController', () => {
|
||||
palette: 'blues',
|
||||
},
|
||||
};
|
||||
const graph = createGraph(options);
|
||||
|
||||
const context = createContext(options);
|
||||
await graph.render();
|
||||
|
||||
const elementController = new ElementController(context);
|
||||
// @ts-expect-error context is private.
|
||||
const elementController = graph.context.element!;
|
||||
|
||||
const edge1Id = idOf(options.data!.edges![0]);
|
||||
const edge2Id = idOf(options.data!.edges![1]);
|
||||
|
||||
// @ts-expect-error computeStyle is private
|
||||
elementController.computeStyle();
|
||||
|
||||
expect(elementController.getDataStyle('node', 'node-1')).toEqual(options.data!.nodes![0].style || {});
|
||||
// 没有属性 / no style
|
||||
expect(elementController.getDataStyle('node', 'node-2')).toEqual({});
|
||||
@ -181,13 +155,14 @@ describe('ElementController', () => {
|
||||
color: BUILT_IN_PALETTES.spectral[2],
|
||||
});
|
||||
|
||||
expect(elementController.getElementComputedStyle('edge', edge1Id)).toEqual({
|
||||
expect(omit(elementController.getElementComputedStyle('edge', edge1Id), ['sourceNode', 'targetNode'])).toEqual({
|
||||
...LIGHT_THEME.edge?.style,
|
||||
sourcePoint: [0, 0, 0],
|
||||
targetPoint: [0, 0, 0],
|
||||
color: BUILT_IN_PALETTES.oranges.at(-1),
|
||||
});
|
||||
expect(elementController.getElementComputedStyle('edge', edge2Id)).toEqual({
|
||||
|
||||
expect(omit(elementController.getElementComputedStyle('edge', edge2Id), ['sourceNode', 'targetNode'])).toEqual({
|
||||
...LIGHT_THEME.edge?.style,
|
||||
...LIGHT_THEME.edge?.state?.active,
|
||||
...LIGHT_THEME.edge?.state?.selected,
|
||||
@ -202,14 +177,13 @@ describe('ElementController', () => {
|
||||
color: BUILT_IN_PALETTES.oranges.at(-2),
|
||||
});
|
||||
|
||||
expect(elementController.getElementComputedStyle('combo', 'combo-1')).toEqual({
|
||||
const comboStyle = elementController.getElementComputedStyle('combo', 'combo-1');
|
||||
|
||||
expect(omit(comboStyle, ['children'])).toEqual({
|
||||
...LIGHT_THEME.combo?.style,
|
||||
color: BUILT_IN_PALETTES.blues[0],
|
||||
children: {
|
||||
// 值为 undefined 是因为在非运行时环境 / The value is undefined because it is not in the runtime environment
|
||||
'node-3': undefined,
|
||||
},
|
||||
});
|
||||
expect(Object.keys(comboStyle.children)).toEqual(['node-3']);
|
||||
});
|
||||
|
||||
it('mock runtime', async () => {
|
||||
@ -228,11 +202,12 @@ describe('ElementController', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const context = createContext(options);
|
||||
const graph = createGraph(options);
|
||||
|
||||
const elementController = new ElementController(context);
|
||||
await graph.render();
|
||||
|
||||
await elementController.render(context);
|
||||
// @ts-expect-error context is private.
|
||||
const elementController = graph.context.element!;
|
||||
|
||||
expect(elementController.getNodes().length).toBe(3);
|
||||
expect(elementController.getEdges().length).toBe(2);
|
||||
|
89
packages/g6/__tests__/unit/runtime/viewport.spec.ts
Normal file
89
packages/g6/__tests__/unit/runtime/viewport.spec.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { Graph } from '../../../src';
|
||||
import data from '../../dataset/cluster.json';
|
||||
import { createGraph } from '../../mock/create';
|
||||
|
||||
const options = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
data,
|
||||
theme: 'light',
|
||||
node: {
|
||||
style: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
state: {
|
||||
active: { fill: '#dbedd0' },
|
||||
},
|
||||
},
|
||||
edge: {
|
||||
style: {},
|
||||
state: {
|
||||
active: { stroke: 'pink', lineWidth: 3 },
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
type: 'd3force',
|
||||
preventOverlap: true,
|
||||
nodeSize: 20,
|
||||
animation: true,
|
||||
},
|
||||
};
|
||||
|
||||
describe('ViewportController', () => {
|
||||
let graph: Graph;
|
||||
beforeAll(async () => {
|
||||
graph = createGraph(options);
|
||||
await graph.render();
|
||||
});
|
||||
|
||||
it('viewport center', () => {
|
||||
expect(graph.getViewportCenter()).toEqual(graph.getPosition());
|
||||
const [x, y] = graph.getViewportCenter();
|
||||
expect(x).toBeCloseTo(250);
|
||||
expect(y).toBeCloseTo(250);
|
||||
});
|
||||
|
||||
it('viewport zoom', async () => {
|
||||
expect(graph.getZoom()).toBe(1);
|
||||
|
||||
await graph.zoomBy(0.5);
|
||||
expect(graph.getZoom()).toBe(0.5);
|
||||
|
||||
await graph.zoomBy(4);
|
||||
expect(graph.getZoom()).toBe(2);
|
||||
|
||||
await graph.zoomTo(1);
|
||||
expect(graph.getZoom()).toBe(1);
|
||||
|
||||
graph.setZoomRange([0.1, 10]);
|
||||
expect(graph.getZoomRange()).toEqual([0.1, 10]);
|
||||
});
|
||||
|
||||
it('viewport translate', async () => {
|
||||
await graph.translateBy([100, 100]);
|
||||
let [x, y] = graph.getPosition();
|
||||
expect(x).toBeCloseTo(350);
|
||||
expect(y).toBeCloseTo(350);
|
||||
|
||||
await graph.translateTo([200, 200]);
|
||||
[x, y] = graph.getPosition();
|
||||
expect(x).toBeCloseTo(450);
|
||||
expect(y).toBeCloseTo(450);
|
||||
});
|
||||
|
||||
it('viewport rotate', async () => {
|
||||
await graph.rotateBy(Math.PI / 4);
|
||||
expect(graph.getRotation()).toBe(Math.PI / 4);
|
||||
|
||||
await graph.rotateBy(Math.PI / 2);
|
||||
expect(graph.getRotation()).toBe((Math.PI * 3) / 4);
|
||||
|
||||
await graph.rotateTo(Math.PI / 2);
|
||||
expect(graph.getRotation()).toBe(Math.PI / 2);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
graph.destroy();
|
||||
});
|
||||
});
|
15
packages/g6/__tests__/unit/utils/delay.spec.ts
Normal file
15
packages/g6/__tests__/unit/utils/delay.spec.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { delay } from '../../../src/utils/delay';
|
||||
|
||||
describe('delay', () => {
|
||||
it('should delay for the specified time', async () => {
|
||||
const startTime = Date.now();
|
||||
const delayTime = 200; // milliseconds
|
||||
|
||||
await delay(delayTime);
|
||||
|
||||
const endTime = Date.now();
|
||||
const elapsedTime = endTime - startTime;
|
||||
|
||||
expect(elapsedTime).toBeGreaterThanOrEqual(delayTime);
|
||||
});
|
||||
});
|
@ -557,6 +557,9 @@ export class ElementController {
|
||||
renderContext,
|
||||
);
|
||||
|
||||
// todo: 不应该返回动画相关的信息,如果确实外部需要,那么应该提供方法获取这类 context 信息。
|
||||
// animation 不是一个需要给开发者强制暴露的信息,因此不应该在这里返回。
|
||||
// updateNodeLikePosition 也是同理,这些都会影响到 Graph API 的封装。
|
||||
return this.postRender(taskId, () => {
|
||||
this.emit(GraphEvent.AFTER_RENDER);
|
||||
});
|
||||
|
@ -348,10 +348,12 @@ export class Graph extends EventEmitter {
|
||||
* <zh/> 绘制元素
|
||||
*
|
||||
* <en/> Draw elements
|
||||
* @returns <zh/> 渲染结果 | <en/> draw result
|
||||
*/
|
||||
public async draw() {
|
||||
await this.prepare();
|
||||
await this.context.element?.render(this.context);
|
||||
// todo: 和 element.draw 一样,不应该返回任何动画相关的信息。
|
||||
return await this.context.element?.render(this.context);
|
||||
}
|
||||
|
||||
public async layout(): Promise<void> {
|
||||
@ -369,11 +371,12 @@ export class Graph extends EventEmitter {
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
const { layout, element, model, canvas } = this.context;
|
||||
const { layout, element, model, canvas, viewport } = this.context;
|
||||
layout?.destroy();
|
||||
element?.destroy();
|
||||
model.destroy();
|
||||
canvas?.destroy();
|
||||
viewport?.destroy();
|
||||
this.options = {};
|
||||
// @ts-expect-error force delete
|
||||
delete this.context;
|
||||
@ -439,6 +442,10 @@ export class Graph extends EventEmitter {
|
||||
return this.context.viewport!.rotate({ mode: 'absolute', value: angle, origin }, effectTiming);
|
||||
}
|
||||
|
||||
public getRotation(): number {
|
||||
return this.context.viewport!.getRotation();
|
||||
}
|
||||
|
||||
public translateBy(
|
||||
offset: Point,
|
||||
origin?: Point,
|
||||
|
@ -7,6 +7,7 @@ import type {
|
||||
ViewportAnimationEffectTiming,
|
||||
ZoomOptions,
|
||||
} from '../types';
|
||||
import { delay } from '../utils/delay';
|
||||
import type { RuntimeContext } from './types';
|
||||
|
||||
export class ViewportController {
|
||||
@ -89,11 +90,14 @@ export class ViewportController {
|
||||
this.context.graph.emit(GraphEvent.BEFORE_VIEWPORT_ANIMATION, options);
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
/**
|
||||
* todo: gotoLandmark 存在问题,有一定概率导致不会触发 onfinish,因此需要设置一个超时时间
|
||||
*/
|
||||
const onfinish = () => {
|
||||
this.context.graph.emit(GraphEvent.AFTER_VIEWPORT_ANIMATION, options);
|
||||
resolve();
|
||||
};
|
||||
resolveWhenTimeout(onfinish, effectTiming.duration);
|
||||
delay(effectTiming.duration).then(onfinish);
|
||||
|
||||
this.camera.gotoLandmark(
|
||||
this.createLandmark(
|
||||
@ -129,7 +133,7 @@ export class ViewportController {
|
||||
this.context.graph.emit(GraphEvent.AFTER_VIEWPORT_ANIMATION, options);
|
||||
resolve();
|
||||
};
|
||||
resolveWhenTimeout(onfinish, effectTiming.duration);
|
||||
delay(effectTiming.duration).then(onfinish);
|
||||
|
||||
this.camera.gotoLandmark(
|
||||
this.createLandmark({ roll: mode === 'relative' ? camera.getRoll() + angle : angle }),
|
||||
@ -163,7 +167,7 @@ export class ViewportController {
|
||||
this.context.graph.emit(GraphEvent.AFTER_VIEWPORT_ANIMATION, options);
|
||||
resolve();
|
||||
};
|
||||
resolveWhenTimeout(onfinish, effectTiming.duration);
|
||||
delay(effectTiming.duration).then(onfinish);
|
||||
|
||||
this.camera.gotoLandmark(this.createLandmark({ zoom: targetRatio }), { ...effectTiming, onfinish });
|
||||
});
|
||||
@ -185,18 +189,3 @@ export class ViewportController {
|
||||
this.cancelAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <zh/> 延迟一段时间后执行 resolve
|
||||
*
|
||||
* <en/> Execute resolve after a period of time
|
||||
* @param resolve - <zh/> resolve 函数 | <en/> resolve function
|
||||
* @param timeout - <zh/> 延迟时间 | <en/> delay time
|
||||
* @description
|
||||
* <zh/> gotoLandmark 存在问题,有一定概率导致不会触发 onfinish,因此需要设置一个超时时间
|
||||
*
|
||||
* <en/> There is a problem with gotoLandmark, which may not trigger onfinish with a certain probability, so a timeout needs to be set
|
||||
*/
|
||||
function resolveWhenTimeout(resolve: () => void, timeout: number = 500) {
|
||||
setTimeout(resolve, timeout);
|
||||
}
|
||||
|
11
packages/g6/src/utils/delay.ts
Normal file
11
packages/g6/src/utils/delay.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* <zh/> 延迟一段时间
|
||||
* <en/> delay a period of time
|
||||
* @param timeout - <zh/> 延迟时间 | <en/> delay time
|
||||
* @returns - <zh/> Promise<void> | <en/> Promise<void>
|
||||
*/
|
||||
export function delay(timeout: number = 500) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, timeout);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user