feat: use serverside rendering for integration tests (#4793)
* feat: use serverside rendering for integration tests * chore: extend svg & webgl matchers for jest * chore: interactive events can be triggerred in test case now * chore: increase timeout * chore: skip WebGL snapshot for now * chore: support animation snapshot in test case * chore: DOM API can be used in test case * fix: make graph.getItemById private
2
.github/workflows/build.yml
vendored
@ -17,8 +17,6 @@ jobs:
|
||||
|
||||
- name: Run CI
|
||||
run: |
|
||||
npm install
|
||||
npm run test
|
||||
cd packages/g6
|
||||
npm install
|
||||
npm run ci
|
4
.gitignore
vendored
@ -27,8 +27,8 @@ coverage
|
||||
stats.html
|
||||
|
||||
# Snapshots error images
|
||||
__tests__/integration/snapshots/**/*-actual.*
|
||||
__tests__/integration/snapshots/**/*-diff.*
|
||||
packages/g6/tests/integration/snapshots/**/*-actual.*
|
||||
packages/g6/tests/integration/snapshots/**/*-diff.*
|
||||
|
||||
# Website cache byb dumi
|
||||
site/.dumi/tmp
|
||||
|
165138
package-lock.json
generated
@ -1,15 +1,13 @@
|
||||
{
|
||||
"name": "g6",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo passed",
|
||||
"postinstall": "husky install",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.1",
|
||||
"@commitlint/cli": "^17.5.0",
|
||||
"@commitlint/config-conventional": "^17.4.4",
|
||||
"husky": "^8.0.3",
|
||||
|
29
packages/g6/jest.node.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
// Installing third-party modules by tnpm or cnpm will name modules with underscore as prefix.
|
||||
// In this case _{module} is also necessary.
|
||||
const esm = ['internmap', 'd3-*', 'lodash-es']
|
||||
.map((d) => `_${d}|${d}`)
|
||||
.join('|');
|
||||
|
||||
module.exports = {
|
||||
testEnvironment: 'jsdom',
|
||||
testTimeout: 100000,
|
||||
preset: 'ts-jest/presets/js-with-ts',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
diagnostics: {
|
||||
exclude: ['**'],
|
||||
},
|
||||
tsconfig: {
|
||||
target: 'esnext', // Increase test coverage.
|
||||
allowJs: true,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
collectCoverageFrom: ['src/**/*.ts'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: false,
|
||||
testRegex: '(/tests/.*\\.(test|spec))\\.(ts|tsx|js)$',
|
||||
// Transform esm to cjs.
|
||||
transformIgnorePatterns: [`<rootDir>/node_modules/(?!(${esm}))`],
|
||||
};
|
@ -36,7 +36,7 @@
|
||||
"build": "run-p build:*",
|
||||
"bundle-vis": "cross-env BUNDLE_VIS=1 run-p build:umd",
|
||||
"prepublishOnly": "npm run ci",
|
||||
"ci": "run-s lint build",
|
||||
"ci": "run-s lint build test:integration",
|
||||
"clean": "rimraf es lib",
|
||||
"coverage": "jest --coverage",
|
||||
"doc": "rimraf apis && typedoc",
|
||||
@ -44,6 +44,7 @@
|
||||
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx",
|
||||
"fix": "eslint ./src ./tests --fix && prettier ./src ./tests --write ",
|
||||
"test": "jest",
|
||||
"test:integration": "node --expose-gc --max-old-space-size=4096 --unhandled-rejections=strict node_modules/jest/bin/jest tests/integration/ --config jest.node.config.js --coverage -i --logHeapUsage --detectOpenHandles",
|
||||
"size": "limit-size",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/edge-spec.ts",
|
||||
"test-behavior": "DEBUG_MODE=1 jest --watch ./tests/unit/item-3d-spec.ts"
|
||||
@ -82,20 +83,31 @@
|
||||
"@antv/g6": "^4.8.13",
|
||||
"@kayahr/jest-electron-runner": "^5.1.1",
|
||||
"@rollup/plugin-terser": "^0.4.3",
|
||||
"@types/gl": "6.0.2",
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/jsdom": "^21.1.1",
|
||||
"@types/node": "13.11.1",
|
||||
"@types/pixelmatch": "^5.2.4",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
"@types/xmlserializer": "^0.6.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"@umijs/fabric": "^2.0.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"canvas": "2.11.0",
|
||||
"eslint": "^7.22.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"father": "^2.29.1",
|
||||
"gl": "^6.0.2",
|
||||
"jest": "^28.1.3",
|
||||
"jest-environment-jsdom": "",
|
||||
"jest-extended": "^0.11.2",
|
||||
"jsdom": "^19.0.0",
|
||||
"limit-size": "^0.1.4",
|
||||
"lint-staged": "^10.5.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pixelmatch": "5.3.0",
|
||||
"pngjs": "^6.0.0",
|
||||
"prettier": "^2.2.1",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^2.79.1",
|
||||
@ -107,7 +119,8 @@
|
||||
"ts-jest": "^28.0.8",
|
||||
"typedoc": "^0.24.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.2.2"
|
||||
"vite": "^4.2.2",
|
||||
"xmlserializer": "^0.6.1"
|
||||
},
|
||||
"limit-size": [
|
||||
{
|
||||
|
@ -144,7 +144,7 @@ export default abstract class Item implements IItem {
|
||||
}
|
||||
}
|
||||
this.renderExt = new RenderExtension({
|
||||
themeStyles: this.themeStyles.default,
|
||||
themeStyles: this.themeStyles?.default,
|
||||
lodStrategy,
|
||||
device: this.device,
|
||||
zoom: this.zoom,
|
||||
|
@ -238,6 +238,9 @@ export default class Node extends Item {
|
||||
point,
|
||||
);
|
||||
break;
|
||||
case 'mesh':
|
||||
intersectPoint = innerPoint;
|
||||
break;
|
||||
default: {
|
||||
const bbox =
|
||||
this.renderExt.boundsCache?.keyShapeLocal ||
|
||||
|
@ -256,11 +256,11 @@ export class InteractionController {
|
||||
this.handleCanvasEvent,
|
||||
);
|
||||
});
|
||||
const $dom = this.graph.canvas.getContextService().getDomElement();
|
||||
Object.values(DOM_EVENT_TYPE).forEach((eventName) => {
|
||||
this.graph.canvas
|
||||
.getContextService()
|
||||
.getDomElement()
|
||||
.addEventListener(eventName, this.handleDOMEvent);
|
||||
if ($dom && $dom.addEventListener) {
|
||||
$dom.addEventListener(eventName, this.handleDOMEvent);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -694,6 +694,8 @@ export class ItemController {
|
||||
|
||||
private onDestroy = () => {
|
||||
Object.values(this.itemMap).forEach((item) => item.destroy());
|
||||
// Fix OOM problem, since this map will hold all the refs of items.
|
||||
this.itemMap = {};
|
||||
};
|
||||
|
||||
private onTransientUpdate(param: {
|
||||
|
@ -42,6 +42,7 @@ export class LayoutController {
|
||||
*/
|
||||
private tap() {
|
||||
this.graph.hooks.layout.tap(this.onLayout.bind(this));
|
||||
this.graph.hooks.destroy.tap(this.onDestroy.bind(this));
|
||||
}
|
||||
|
||||
private async onLayout(params: {
|
||||
@ -189,7 +190,11 @@ export class LayoutController {
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
getCurrentAnimation() {
|
||||
return this.currentAnimation;
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.stopLayout();
|
||||
|
||||
if (this.currentSupervisor) {
|
||||
|
@ -54,10 +54,14 @@ export class ThemeController {
|
||||
if (this.extension) {
|
||||
this.solver = new this.extension(this.themeConfig, this.themes);
|
||||
this.specification = this.solver.specification;
|
||||
// apply canvas style in theme to the background canvas dom
|
||||
const { canvas } = this.specification;
|
||||
const dom = canvases.background.getContextService().getDomElement();
|
||||
Object.keys(canvas).forEach((key) => (dom.style[key] = canvas[key]));
|
||||
if (this.specification) {
|
||||
// apply canvas style in theme to the background canvas dom
|
||||
const { canvas } = this.specification;
|
||||
const dom = canvases.background.getContextService().getDomElement();
|
||||
if (dom && dom.style) {
|
||||
Object.keys(canvas).forEach((key) => (dom.style[key] = canvas[key]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,8 +124,17 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
}
|
||||
|
||||
private initCanvas() {
|
||||
const { renderer, container, width, height } = this.specification;
|
||||
let pixelRatio;
|
||||
const {
|
||||
renderer,
|
||||
container,
|
||||
canvas,
|
||||
backgroundCanvas,
|
||||
transientCanvas,
|
||||
width,
|
||||
height,
|
||||
} = this.specification;
|
||||
|
||||
let pixelRatio: number;
|
||||
if (renderer && !isString(renderer)) {
|
||||
// @ts-ignore
|
||||
this.rendererType = renderer.type || 'canvas';
|
||||
@ -135,55 +144,80 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
// @ts-ignore
|
||||
this.rendererType = renderer || 'canvas';
|
||||
}
|
||||
const containerDOM = isString(container)
|
||||
? document.getElementById(container as string)
|
||||
: container;
|
||||
if (!containerDOM) {
|
||||
console.error(
|
||||
`Create graph failed. The container for graph ${containerDOM} is not exist.`,
|
||||
|
||||
/**
|
||||
* These 3 canvases can be passed in by users, e.g. when doing serverside rendering we can't use DOM API.
|
||||
*/
|
||||
if (canvas) {
|
||||
this.canvas = canvas;
|
||||
this.backgroundCanvas = backgroundCanvas;
|
||||
this.transientCanvas = transientCanvas;
|
||||
} else {
|
||||
const containerDOM = isString(container)
|
||||
? document.getElementById(container as string)
|
||||
: (container as HTMLElement);
|
||||
if (!containerDOM) {
|
||||
console.error(
|
||||
`Create graph failed. The container for graph ${containerDOM} is not exist.`,
|
||||
);
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
this.container = containerDOM;
|
||||
const size = [width, height];
|
||||
if (size[0] === undefined) {
|
||||
size[0] = containerDOM.scrollWidth;
|
||||
}
|
||||
if (size[1] === undefined) {
|
||||
size[1] = containerDOM.scrollHeight;
|
||||
}
|
||||
this.backgroundCanvas = createCanvas(
|
||||
this.rendererType,
|
||||
containerDOM,
|
||||
size[0],
|
||||
size[1],
|
||||
pixelRatio,
|
||||
);
|
||||
this.canvas = createCanvas(
|
||||
this.rendererType,
|
||||
containerDOM,
|
||||
size[0],
|
||||
size[1],
|
||||
pixelRatio,
|
||||
);
|
||||
this.transientCanvas = createCanvas(
|
||||
this.rendererType,
|
||||
containerDOM,
|
||||
size[0],
|
||||
size[1],
|
||||
pixelRatio,
|
||||
);
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
this.container = containerDOM;
|
||||
const size = [width, height];
|
||||
if (size[0] === undefined) {
|
||||
size[0] = containerDOM.scrollWidth;
|
||||
}
|
||||
if (size[1] === undefined) {
|
||||
size[1] = containerDOM.scrollHeight;
|
||||
}
|
||||
|
||||
this.backgroundCanvas = createCanvas(
|
||||
this.rendererType,
|
||||
containerDOM,
|
||||
size[0],
|
||||
size[1],
|
||||
pixelRatio,
|
||||
);
|
||||
this.canvas = createCanvas(
|
||||
this.rendererType,
|
||||
containerDOM,
|
||||
size[0],
|
||||
size[1],
|
||||
pixelRatio,
|
||||
);
|
||||
this.transientCanvas = createCanvas(
|
||||
this.rendererType,
|
||||
containerDOM,
|
||||
size[0],
|
||||
size[1],
|
||||
pixelRatio,
|
||||
true,
|
||||
{
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
);
|
||||
Promise.all(
|
||||
[this.backgroundCanvas, this.canvas, this.transientCanvas].map(
|
||||
(canvas) => canvas.ready,
|
||||
),
|
||||
).then(() => (this.canvasReady = true));
|
||||
).then(() => {
|
||||
[this.backgroundCanvas, this.canvas, this.transientCanvas].forEach(
|
||||
(canvas, i) => {
|
||||
const $domElement = canvas
|
||||
.getContextService()
|
||||
.getDomElement() as unknown as HTMLElement;
|
||||
if ($domElement && $domElement.style) {
|
||||
$domElement.style.position = 'fixed';
|
||||
$domElement.style.outline = 'none';
|
||||
$domElement.tabIndex = 1; // Enable keyboard events
|
||||
// Transient canvas should let interactive events go through.
|
||||
if (i === 2) {
|
||||
$domElement.style.pointerEvents = 'none';
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.canvasReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -623,7 +657,7 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
public async focusItem(id: ID | ID[], effectTiming?: CameraAnimationOptions) {
|
||||
let bounds: AABB | null = null;
|
||||
for (const itemId of !isArray(id) ? [id] : id) {
|
||||
const item = this.itemController.getItemById(itemId);
|
||||
const item = this.getItemById(itemId);
|
||||
if (item) {
|
||||
const itemBounds = item.group.getBounds();
|
||||
if (!bounds) {
|
||||
@ -645,6 +679,15 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item by id. We don't want to
|
||||
* @param id
|
||||
* @returns Node | Edge | Combo
|
||||
*/
|
||||
private getItemById(id: ID) {
|
||||
return this.itemController.getItemById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the graph canvas.
|
||||
* @returns [width, height]
|
||||
@ -1368,6 +1411,14 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
this.layoutController.stopLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
public getLayoutCurrentAnimation() {
|
||||
return this.layoutController.getCurrentAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch mode.
|
||||
* @param mode mode name
|
||||
@ -1609,12 +1660,12 @@ export default class Graph<B extends BehaviorRegistry, T extends ThemeRegistry>
|
||||
*/
|
||||
public destroy(callback?: Function) {
|
||||
// TODO: call the destroy functions after items' buildOut animations finished
|
||||
setTimeout(() => {
|
||||
this.canvas.destroy();
|
||||
this.backgroundCanvas.destroy();
|
||||
this.transientCanvas.destroy();
|
||||
callback?.();
|
||||
}, 500);
|
||||
// setTimeout(() => {
|
||||
this.canvas.destroy();
|
||||
this.backgroundCanvas.destroy();
|
||||
this.transientCanvas.destroy();
|
||||
callback?.();
|
||||
// }, 500);
|
||||
|
||||
this.hooks.destroy.emit({});
|
||||
|
||||
|
@ -58,7 +58,6 @@ const DEFAULT_OPTIONS: ActivateRelationsOptions = {
|
||||
};
|
||||
|
||||
export default class ActivateRelations extends Behavior {
|
||||
options: ActivateRelationsOptions;
|
||||
timer: number;
|
||||
inactiveItems: {};
|
||||
prevNodeIds: ID[];
|
||||
|
@ -99,7 +99,6 @@ const DEFAULT_OPTIONS: BrushSelectOptions = {
|
||||
};
|
||||
|
||||
export default class BrushSelect extends Behavior {
|
||||
options: BrushSelectOptions;
|
||||
brush: DisplayObject | undefined;
|
||||
selectedIds: IDSet | undefined = {
|
||||
nodes: [],
|
||||
|
@ -58,7 +58,6 @@ const DEFAULT_OPTIONS: ClickSelectOptions = {
|
||||
};
|
||||
|
||||
export default class ClickSelect extends Behavior {
|
||||
options: ClickSelectOptions;
|
||||
/**
|
||||
* Cache the ids of items selected by this behavior
|
||||
*/
|
||||
|
@ -28,8 +28,6 @@ const DEFAULT_OPTIONS: Required<CollapseExpandComboOptions> = {
|
||||
};
|
||||
|
||||
export default class CollapseExpandCombo extends Behavior {
|
||||
options: CollapseExpandComboOptions;
|
||||
|
||||
constructor(options: Partial<CollapseExpandComboOptions>) {
|
||||
const finalOptions = Object.assign({}, DEFAULT_OPTIONS, options);
|
||||
super(finalOptions);
|
||||
|
@ -66,8 +66,6 @@ const DEFAULT_OPTIONS: Required<DragCanvasOptions> = {
|
||||
};
|
||||
|
||||
export default class DragCanvas extends Behavior {
|
||||
options: DragCanvasOptions;
|
||||
|
||||
private pointerDownAt: Point;
|
||||
private dragging: boolean; // pointerdown + pointermove a distance
|
||||
private keydown: boolean;
|
||||
|
@ -82,8 +82,6 @@ const DEFAULT_OPTIONS: Required<DragComboOptions> = {
|
||||
};
|
||||
|
||||
export default class DragCombo extends Behavior {
|
||||
options: DragComboOptions;
|
||||
|
||||
// Private states
|
||||
private hiddenEdges: EdgeModel[] = [];
|
||||
private hiddenComboTreeRoots: (ComboModel | NodeModel)[] = [];
|
||||
|
@ -87,8 +87,6 @@ const DEFAULT_OPTIONS: Required<DragNodeOptions> = {
|
||||
};
|
||||
|
||||
export default class DragNode extends Behavior {
|
||||
options: DragNodeOptions;
|
||||
|
||||
// Private states
|
||||
private hiddenEdges: EdgeModel[] = [];
|
||||
private hiddenComboTreeItems: (ComboModel | NodeModel)[] = [];
|
||||
|
@ -39,7 +39,6 @@ const DEFAULT_OPTIONS: Required<HoverActivateOptions> = {
|
||||
};
|
||||
|
||||
export default class HoverActivate extends Behavior {
|
||||
options: HoverActivateOptions;
|
||||
private currentItemInfo: { id: ID; itemType: ITEM_TYPE };
|
||||
|
||||
constructor(options: Partial<HoverActivateOptions>) {
|
||||
|
@ -39,8 +39,6 @@ const DEFAULT_OPTIONS: Required<OrbitCanvas3DOptions> = {
|
||||
* Translate the 3d canvas along the plane parallel to the screen.
|
||||
*/
|
||||
export default class OrbitCanvas3D extends RotateCanvas3D {
|
||||
options: OrbitCanvas3DOptions;
|
||||
|
||||
private previousType: CameraType;
|
||||
|
||||
constructor(options: Partial<OrbitCanvas3DOptions>) {
|
||||
|
@ -50,8 +50,6 @@ const MOTION_FACTOR = 10;
|
||||
* Rotate the 3d canvas with the center of the graph.
|
||||
*/
|
||||
export default class RotateCanvas3D extends Behavior {
|
||||
options: RotateCanvas3DOptions;
|
||||
|
||||
public pointStartAt: Point;
|
||||
public keydown: boolean;
|
||||
public speedUpKeydown: boolean;
|
||||
|
@ -39,8 +39,6 @@ const DEFAULT_OPTIONS: Required<TrackCanvas3DOptions> = {
|
||||
* Translate the 3d canvas along the plane parallel to the screen.
|
||||
*/
|
||||
export default class TrackCanvas3D extends RotateCanvas3D {
|
||||
options: TrackCanvas3DOptions;
|
||||
|
||||
private previousType: CameraType;
|
||||
|
||||
constructor(options: Partial<TrackCanvas3DOptions>) {
|
||||
|
@ -60,8 +60,6 @@ const DEFAULT_OPTIONS: Required<ZoomCanvas3DOptions> = {
|
||||
* Zoom the 3d canvas along the ray vertical to the screen.
|
||||
*/
|
||||
export default class ZoomCanvas3D extends Behavior {
|
||||
options: ZoomCanvas3DOptions;
|
||||
|
||||
private keydown: boolean;
|
||||
|
||||
constructor(options: Partial<ZoomCanvas3DOptions>) {
|
||||
@ -80,16 +78,16 @@ export default class ZoomCanvas3D extends Behavior {
|
||||
}
|
||||
|
||||
getEvents = () => {
|
||||
this.graph.canvas
|
||||
.getContextService()
|
||||
.getDomElement()
|
||||
.addEventListener(
|
||||
const $el = this.graph.canvas.getContextService().getDomElement();
|
||||
if ($el && $el.addEventListener) {
|
||||
$el.addEventListener(
|
||||
'wheel',
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
},
|
||||
{ passive: false },
|
||||
);
|
||||
}
|
||||
|
||||
if (this.options.trigger === 'wheel') {
|
||||
return {
|
||||
|
@ -69,8 +69,6 @@ const DEFAULT_OPTIONS: Required<ZoomCanvasOptions> = {
|
||||
};
|
||||
|
||||
export default class ZoomCanvas extends Behavior {
|
||||
options: ZoomCanvasOptions;
|
||||
|
||||
private zooming: boolean; // pointerdown + pointermove a distance
|
||||
private keydown: boolean;
|
||||
private speedupKeydown: boolean;
|
||||
|
@ -282,6 +282,7 @@ export abstract class BaseEdge {
|
||||
...this.defaultStyles.labelShape,
|
||||
textAlign: positionPreset.textAlign,
|
||||
wordWrapWidth,
|
||||
isBillboard: true,
|
||||
...positionStyle,
|
||||
...otherStyle,
|
||||
};
|
||||
@ -527,7 +528,7 @@ export abstract class BaseEdge {
|
||||
const { labelShape, labelBackgroundShape } = shapeMap;
|
||||
const balanceRatio = 1 / zoom || 1;
|
||||
this.zoomCache.balanceRatio = balanceRatio;
|
||||
const { labelShape: labelStyle } = this.mergedStyles;
|
||||
const { labelShape: labelStyle = {} } = this.mergedStyles;
|
||||
const { position = 'bottom' } = labelStyle;
|
||||
if (!labelShape) return;
|
||||
|
||||
|
@ -86,10 +86,11 @@ export class LineEdge extends BaseEdge {
|
||||
...keyShapeStyle,
|
||||
x1: sourcePoint.x,
|
||||
y1: sourcePoint.y,
|
||||
z1: sourcePoint.z,
|
||||
z1: sourcePoint.z || 0,
|
||||
x2: targetPoint.x,
|
||||
y2: targetPoint.y,
|
||||
z2: targetPoint.z,
|
||||
z2: targetPoint.z || 0,
|
||||
isBillboard: true,
|
||||
},
|
||||
shapeMap,
|
||||
model,
|
||||
|
@ -300,6 +300,7 @@ export abstract class BaseNode {
|
||||
const style: any = {
|
||||
...this.defaultStyles.labelShape,
|
||||
...positionPreset,
|
||||
isBillboard: true,
|
||||
...otherStyle,
|
||||
};
|
||||
return this.upsertShape('text', 'labelShape', style, shapeMap, model);
|
||||
|
@ -102,8 +102,10 @@ export abstract class BaseNode3D extends BaseNode {
|
||||
const style: any = {
|
||||
...this.defaultStyles.labelShape,
|
||||
...positionPreset,
|
||||
isBillboard: true,
|
||||
...otherStyle,
|
||||
};
|
||||
|
||||
return this.upsertShape('text', 'labelShape', style, shapeMap);
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export default class Grid extends Base {
|
||||
public getDefaultCfgs(): GridConfig {
|
||||
return {
|
||||
img: GRID_PNG,
|
||||
follow: true
|
||||
follow: true,
|
||||
};
|
||||
}
|
||||
|
||||
@ -71,9 +71,9 @@ export default class Grid extends Base {
|
||||
left: `0px`,
|
||||
top: `0px`,
|
||||
});
|
||||
|
||||
graphContainer.insertBefore(container, canvas);
|
||||
|
||||
|
||||
graphContainer.insertBefore(container, canvas as Node);
|
||||
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@ -94,8 +94,9 @@ export default class Grid extends Base {
|
||||
if (!matrix) matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
|
||||
|
||||
const isFollow = this.options.follow;
|
||||
const transform = `matrix(${matrix[0]}, ${matrix[1]}, ${matrix[3]}, ${matrix[4]}, ${isFollow ? matrix[6] : '0'
|
||||
}, ${isFollow ? matrix[7] : '0'})`;
|
||||
const transform = `matrix(${matrix[0]}, ${matrix[1]}, ${matrix[3]}, ${
|
||||
matrix[4]
|
||||
}, ${isFollow ? matrix[6] : '0'}, ${isFollow ? matrix[7] : '0'})`;
|
||||
|
||||
modifyCSS(gridContainer, {
|
||||
transform,
|
||||
|
@ -31,9 +31,6 @@ interface SpecThemeSolverOptions {
|
||||
};
|
||||
}
|
||||
export default class SpecThemeSolver extends BaseThemeSolver {
|
||||
protected specification: ThemeSpecification;
|
||||
public options: SpecThemeSolverOptions;
|
||||
|
||||
public solver(
|
||||
options: SpecThemeSolverOptions,
|
||||
themes: ThemeSpecificationMap,
|
||||
|
@ -28,9 +28,6 @@ interface SubjectThemeSolverOptions {
|
||||
}
|
||||
|
||||
export default class SubjectThemeSolver extends BaseThemeSolver {
|
||||
protected specification: ThemeSpecification;
|
||||
public options: SubjectThemeSolverOptions;
|
||||
|
||||
public solver(
|
||||
options: SubjectThemeSolverOptions,
|
||||
themes: ThemeSpecificationMap,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { uniqueId } from '@antv/util';
|
||||
import { IG6GraphEvent } from './event';
|
||||
import { IGraph } from './graph';
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Canvas } from '@antv/g';
|
||||
import { AnimateCfg } from './animate';
|
||||
import { Point } from './common';
|
||||
import {
|
||||
@ -36,7 +37,10 @@ export interface Specification<
|
||||
T extends ThemeRegistry,
|
||||
> {
|
||||
type: 'graph' | 'tree';
|
||||
container: string | HTMLElement;
|
||||
container?: string | HTMLElement;
|
||||
backgroundCanvas?: Canvas;
|
||||
canvas?: Canvas;
|
||||
transientCanvas?: Canvas;
|
||||
width?: number;
|
||||
height?: number;
|
||||
renderer?:
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Canvas, CanvasLike } from '@antv/g';
|
||||
import { Canvas, IRenderer } from '@antv/g';
|
||||
import { Renderer as CanvasRenderer } from '@antv/g-canvas';
|
||||
import { Renderer as SVGRenderer } from '@antv/g-svg';
|
||||
import { Renderer as WebGLRenderer } from '@antv/g-webgl';
|
||||
@ -13,7 +13,6 @@ import { RendererName } from '../types/render';
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @param {number} pixelRatio optional
|
||||
* @param {boolean} customCanvasTag whether create a <canvas /> for multiple canvas under the container
|
||||
* @returns
|
||||
*/
|
||||
export const createCanvas = (
|
||||
@ -22,10 +21,8 @@ export const createCanvas = (
|
||||
width: number,
|
||||
height: number,
|
||||
pixelRatio?: number,
|
||||
customCanvasTag = true,
|
||||
style: any = {},
|
||||
): Canvas => {
|
||||
let renderer;
|
||||
let renderer: IRenderer;
|
||||
switch (rendererType.toLowerCase()) {
|
||||
case 'svg':
|
||||
renderer = new SVGRenderer();
|
||||
@ -48,30 +45,13 @@ export const createCanvas = (
|
||||
}),
|
||||
);
|
||||
|
||||
if (typeof document !== 'undefined' && customCanvasTag) {
|
||||
const canvasTag = document.createElement('canvas');
|
||||
const dpr = pixelRatio || window.devicePixelRatio;
|
||||
canvasTag.width = dpr * width;
|
||||
canvasTag.height = dpr * height;
|
||||
canvasTag.style.width = `${width}px`;
|
||||
canvasTag.style.height = `${height}px`;
|
||||
canvasTag.style.position = 'fixed';
|
||||
canvasTag.style.outline = 'none';
|
||||
canvasTag.tabIndex = 1; // Enable keyboard events
|
||||
Object.assign(canvasTag.style, style);
|
||||
container!.appendChild(canvasTag);
|
||||
return new Canvas({
|
||||
canvas: canvasTag as CanvasLike,
|
||||
devicePixelRatio: pixelRatio,
|
||||
renderer,
|
||||
});
|
||||
}
|
||||
return new Canvas({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
devicePixelRatio: pixelRatio,
|
||||
renderer,
|
||||
supportsMutipleCanvasesInOneContainer: true,
|
||||
});
|
||||
};
|
||||
|
||||
@ -85,7 +65,7 @@ export const changeRenderer = (
|
||||
rendererType: RendererName,
|
||||
canvas: Canvas,
|
||||
): Canvas => {
|
||||
let renderer;
|
||||
let renderer: IRenderer;
|
||||
switch (rendererType.toLowerCase()) {
|
||||
case 'svg':
|
||||
renderer = new SVGRenderer();
|
||||
|
35
packages/g6/tests/demo/animations/node-build-in.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const { width, height } = context;
|
||||
return new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'circular',
|
||||
center: [width! / 2, height! / 2],
|
||||
radius: 200,
|
||||
},
|
||||
node: (innerModel) => {
|
||||
return {
|
||||
...innerModel,
|
||||
data: {
|
||||
...innerModel.data,
|
||||
animates: {
|
||||
buildIn: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
duration: 2000,
|
||||
fill: 'both',
|
||||
delay: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
@ -1,15 +1,13 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { container, height, width } from '../../datasets/const';
|
||||
export default () => {
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
return new G6.Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
...context,
|
||||
type: 'graph',
|
||||
layout: {
|
||||
type: 'grid',
|
||||
},
|
||||
plugins: ['grid'],
|
||||
node: {
|
||||
labelShape: {
|
||||
text: {
|
@ -2,7 +2,12 @@ import behaviors_activateRelations from './behaviors/activate-relations';
|
||||
import behaviors_brush_select from './behaviors/brush-select';
|
||||
import behaviors_click_select from './behaviors/click-select';
|
||||
import layouts_circular from './layouts/circular';
|
||||
import layouts_grid from './layouts/grid';
|
||||
import layouts_dagre from './layouts/dagre';
|
||||
import layouts_force from './layouts/force';
|
||||
import layouts_d3force from './layouts/d3force';
|
||||
import layouts_custom from './layouts/custom';
|
||||
import user_defined_canvas from './user-defined-canvas/circular';
|
||||
import layouts_fruchterman_wasm from './layouts/fruchterman-wasm';
|
||||
import layouts_forceatlas2_wasm from './layouts/forceatlas2-wasm';
|
||||
import layouts_force_wasm from './layouts/force-wasm';
|
||||
@ -26,11 +31,17 @@ import cubic_vertical_edge from './item/edge/cubic-vertical-edge';
|
||||
import fisheye from './plugins/fisheye';
|
||||
import tooltip from './demo/tooltip';
|
||||
import comboBasic from './combo/combo-basic';
|
||||
import animations_node_build_in from './animations/node-build-in';
|
||||
|
||||
export {
|
||||
behaviors_activateRelations,
|
||||
layouts_circular,
|
||||
layouts_grid,
|
||||
layouts_dagre,
|
||||
layouts_force,
|
||||
layouts_d3force,
|
||||
layouts_custom,
|
||||
user_defined_canvas,
|
||||
layouts_fruchterman_wasm,
|
||||
layouts_forceatlas2_wasm,
|
||||
layouts_force_wasm,
|
||||
@ -56,4 +67,5 @@ export {
|
||||
fisheye,
|
||||
tooltip,
|
||||
comboBasic,
|
||||
animations_node_build_in,
|
||||
};
|
12
packages/g6/tests/demo/interface.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Canvas } from '@antv/g';
|
||||
import { RendererName } from '../../src/types/render';
|
||||
|
||||
export type TestCaseContext = Partial<{
|
||||
container: HTMLElement;
|
||||
renderer: RendererName;
|
||||
canvas: Canvas;
|
||||
transientCanvas: Canvas;
|
||||
backgroundCanvas: Canvas;
|
||||
width: number;
|
||||
height: number;
|
||||
}>;
|
@ -1,4 +1,5 @@
|
||||
import { Graph, IGraph } from '../../../../src/index';
|
||||
import { TestCaseContext } from '../../interface';
|
||||
// @ts-nocheck
|
||||
|
||||
let graph: IGraph;
|
||||
@ -48,16 +49,14 @@ const defaultData = {
|
||||
};
|
||||
|
||||
// create container for controllers
|
||||
const createCtrlContainer = () => {
|
||||
const container = document.getElementById('container')!;
|
||||
const createCtrlContainer = (container: HTMLElement) => {
|
||||
const ctrlContainer = document.createElement('div');
|
||||
ctrlContainer.id = 'ctrl-container';
|
||||
ctrlContainer.style.width = '100%';
|
||||
ctrlContainer.style.height = '200px';
|
||||
ctrlContainer.style.backgroundColor = '#eee';
|
||||
|
||||
const appElement = document.getElementById('app')!;
|
||||
appElement.insertBefore(ctrlContainer, container);
|
||||
container.appendChild(ctrlContainer);
|
||||
};
|
||||
|
||||
// Create options and control buttons (for selecting different features to test)
|
||||
@ -83,7 +82,6 @@ const createControls = () => {
|
||||
labelCb.style.zIndex = '100';
|
||||
|
||||
labelCb.addEventListener('click', (e) => {
|
||||
console.log(labelCb.checked);
|
||||
if (labelCb.checked) {
|
||||
graph.updateData('edge', {
|
||||
id: 'edge1',
|
||||
@ -127,7 +125,6 @@ const createControls = () => {
|
||||
iconCb.style.zIndex = '100';
|
||||
|
||||
iconCb.addEventListener('click', (e) => {
|
||||
console.log(iconCb.checked);
|
||||
if (iconCb.checked) {
|
||||
graph.updateData('edge', {
|
||||
id: 'edge1',
|
||||
@ -180,7 +177,6 @@ const createControls = () => {
|
||||
selectedStyleCb.style.zIndex = '100';
|
||||
|
||||
selectedStyleCb.addEventListener('click', (e) => {
|
||||
console.log(selectedStyleCb.checked);
|
||||
if (selectedStyleCb.checked) {
|
||||
graph.setItemState('edge1', 'selected', true);
|
||||
} else {
|
||||
@ -210,7 +206,6 @@ const createControls = () => {
|
||||
highlightStyleCb.style.zIndex = '100';
|
||||
|
||||
highlightStyleCb.addEventListener('click', (e) => {
|
||||
console.log(highlightStyleCb.checked);
|
||||
if (highlightStyleCb.checked) {
|
||||
graph.setItemState('edge1', 'highlight', true);
|
||||
} else {
|
||||
@ -222,17 +217,16 @@ const createControls = () => {
|
||||
parentEle.appendChild(highlightStyleCb);
|
||||
};
|
||||
|
||||
export default () => {
|
||||
export default (context: TestCaseContext) => {
|
||||
const { container } = context;
|
||||
|
||||
// 1.create control container (for control buttons, etc.)
|
||||
createCtrlContainer();
|
||||
createCtrlContainer(container!);
|
||||
createControls();
|
||||
|
||||
// 2.create graph
|
||||
container = document.getElementById('container')!;
|
||||
graph = new Graph({
|
||||
container,
|
||||
width: 500,
|
||||
height: 500,
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: defaultData,
|
||||
modes: {
|
17
packages/g6/tests/demo/layouts/circular.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const { width, height } = context;
|
||||
return new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'circular',
|
||||
center: [width! / 2, height! / 2],
|
||||
radius: 200,
|
||||
},
|
||||
});
|
||||
};
|
40
packages/g6/tests/demo/layouts/custom.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Layout, LayoutMapping } from '@antv/layout';
|
||||
import G6 from '../../../src/index';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
class MyCustomLayout implements Layout<{}> {
|
||||
async assign(graph, options?: {}): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
async execute(graph, options?: {}): Promise<LayoutMapping> {
|
||||
const nodes = graph.getAllNodes();
|
||||
return {
|
||||
nodes: nodes.map((node, i) => ({
|
||||
id: node.id,
|
||||
data: {
|
||||
x: 0 + i * 10,
|
||||
y: 250,
|
||||
z: 0,
|
||||
},
|
||||
})),
|
||||
edges: [],
|
||||
};
|
||||
}
|
||||
options: {};
|
||||
id: 'myCustomLayout';
|
||||
}
|
||||
|
||||
// Register custom layout
|
||||
G6.stdLib.layouts['myCustomLayout'] = MyCustomLayout;
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
return new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'myCustomLayout',
|
||||
},
|
||||
});
|
||||
};
|
@ -1,17 +1,17 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { container, data, height, width } from '../../datasets/const';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default () => {
|
||||
export default (context: TestCaseContext) => {
|
||||
const { width, height } = context;
|
||||
return new G6.Graph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'd3force',
|
||||
animated: true,
|
||||
center: [250, 250],
|
||||
center: [width! / 2, height! / 2],
|
||||
preventOverlap: true,
|
||||
nodeSize: 20,
|
||||
},
|
121
packages/g6/tests/demo/layouts/dagre.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
data: {
|
||||
name: 'alps_file1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
data: {
|
||||
name: 'alps_file2',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
data: {
|
||||
name: 'alps_file3',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
data: {
|
||||
name: 'sql_file1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
data: {
|
||||
name: 'sql_file2',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
data: {
|
||||
name: 'feature_etl_1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
data: {
|
||||
name: 'feature_etl_1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
data: {
|
||||
name: 'feature_extractor',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'e1',
|
||||
data: {},
|
||||
source: '1',
|
||||
target: '2',
|
||||
},
|
||||
{
|
||||
id: 'e2',
|
||||
data: {},
|
||||
source: '1',
|
||||
target: '3',
|
||||
},
|
||||
{
|
||||
id: 'e3',
|
||||
data: {},
|
||||
source: '2',
|
||||
target: '4',
|
||||
},
|
||||
{
|
||||
id: 'e4',
|
||||
data: {},
|
||||
source: '3',
|
||||
target: '4',
|
||||
},
|
||||
{
|
||||
id: 'e5',
|
||||
data: {},
|
||||
source: '4',
|
||||
target: '5',
|
||||
},
|
||||
{
|
||||
id: 'e6',
|
||||
data: {},
|
||||
source: '5',
|
||||
target: '6',
|
||||
},
|
||||
{
|
||||
id: 'e7',
|
||||
data: {},
|
||||
source: '6',
|
||||
target: '7',
|
||||
},
|
||||
{
|
||||
id: 'e8',
|
||||
data: {},
|
||||
source: '6',
|
||||
target: '8',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'dagre',
|
||||
nodeSize: 10,
|
||||
ranksep: 20,
|
||||
controlPoints: true,
|
||||
begin: [20, 20],
|
||||
align: 'UR',
|
||||
},
|
||||
});
|
||||
};
|
56
packages/g6/tests/demo/layouts/force-3d.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const { width, height } = context;
|
||||
const graph = new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
renderer: 'webgl-3d',
|
||||
// modes: {
|
||||
// default: [
|
||||
// {
|
||||
// type: 'orbit-canvas-3d',
|
||||
// trigger: 'drag',
|
||||
// },
|
||||
// 'zoom-canvas-3d',
|
||||
// ],
|
||||
// },
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'force',
|
||||
dimensions: 3,
|
||||
iterations: 100,
|
||||
center: [width! / 2, height! / 2, 0],
|
||||
},
|
||||
edge: {
|
||||
type: 'line-edge',
|
||||
keyShape: {
|
||||
lineWidth: 2,
|
||||
stroke: 'grey',
|
||||
},
|
||||
},
|
||||
node: {
|
||||
type: 'sphere-node',
|
||||
keyShape: {
|
||||
opacity: 0.6,
|
||||
r: 10,
|
||||
},
|
||||
labelShape: {
|
||||
text: {
|
||||
fields: ['id'],
|
||||
formatter: (model) => model.id,
|
||||
},
|
||||
fontSize: 4,
|
||||
wordWrapWidth: 200,
|
||||
isSizeAttenuation: true,
|
||||
},
|
||||
// iconShape: {
|
||||
// img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
// },
|
||||
},
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
18
packages/g6/tests/demo/layouts/force.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const { width, height } = context;
|
||||
return new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'force',
|
||||
center: [width! / 2, height! / 2, 0],
|
||||
preventOverlap: true,
|
||||
nodeSize: 20,
|
||||
},
|
||||
});
|
||||
};
|
16
packages/g6/tests/demo/layouts/grid.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import G6 from '../../../src/index';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
import { TestCaseContext } from '../interface';
|
||||
|
||||
export default (context: TestCaseContext) => {
|
||||
const { width, height } = context;
|
||||
return new G6.Graph({
|
||||
...context,
|
||||
type: 'graph',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
layout: {
|
||||
type: 'grid',
|
||||
center: [width! / 2, height! / 2],
|
||||
},
|
||||
});
|
||||
};
|
75
packages/g6/tests/demo/user-defined-canvas/circular.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { Canvas } from '@antv/g';
|
||||
import { Renderer } from '@antv/g-canvas';
|
||||
import G6 from '../../../src/index';
|
||||
import { RendererName } from '../../../src/types/render';
|
||||
import { data } from '../../datasets/dataset1';
|
||||
|
||||
export default ({
|
||||
container,
|
||||
renderer,
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
container: HTMLElement;
|
||||
renderer: RendererName;
|
||||
canvas: Canvas;
|
||||
transientCanvas: Canvas;
|
||||
backgroundCanvas: Canvas;
|
||||
width: number;
|
||||
height: number;
|
||||
}) => {
|
||||
const $backgroundCanvas = document.createElement('canvas');
|
||||
const $canvas = document.createElement('canvas');
|
||||
const $transientCanvas = document.createElement('canvas');
|
||||
container?.appendChild($backgroundCanvas);
|
||||
container?.appendChild($canvas);
|
||||
container?.appendChild($transientCanvas);
|
||||
|
||||
[$backgroundCanvas, $canvas, $transientCanvas].forEach(($domElement, i) => {
|
||||
$domElement.width = 2 * 500;
|
||||
$domElement.height = 2 * 500;
|
||||
$domElement.style.width = '500px';
|
||||
$domElement.style.height = '500px';
|
||||
$domElement.style.position = 'fixed';
|
||||
$domElement.style.outline = 'none';
|
||||
$domElement.tabIndex = 1; // Enable keyboard events
|
||||
// Transient canvas should let interactive events go through.
|
||||
if (i === 2) {
|
||||
$domElement.style.pointerEvents = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
const backgroundCanvas = new Canvas({
|
||||
canvas: $backgroundCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
renderer: new Renderer(),
|
||||
});
|
||||
const canvas = new Canvas({
|
||||
canvas: $canvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
renderer: new Renderer(),
|
||||
});
|
||||
const transientCanvas = new Canvas({
|
||||
canvas: $transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
renderer: new Renderer(),
|
||||
});
|
||||
|
||||
return new G6.Graph({
|
||||
canvas,
|
||||
transientCanvas,
|
||||
backgroundCanvas,
|
||||
width,
|
||||
height,
|
||||
type: 'graph',
|
||||
data,
|
||||
layout: {
|
||||
type: 'circular',
|
||||
center: [250, 250],
|
||||
radius: 200,
|
||||
},
|
||||
});
|
||||
};
|
@ -8,8 +8,14 @@
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<label for="">选择要测试的DEMO </label>
|
||||
<select id="select" style="height: 40px; cursor: pointer"></select>
|
||||
<label for="demo-select">选择 DEMO </label>
|
||||
<select id="demo-select" style="height: 40px; cursor: pointer"></select>
|
||||
<label for="renderer-select">选择 Renderer </label>
|
||||
<select id="renderer-select" style="height: 40px; cursor: pointer">
|
||||
<option value="canvas" selected>canvas</option>
|
||||
<option value="svg">svg</option>
|
||||
<option value="webgl">webgl</option>
|
||||
</select>
|
||||
<div id="container" style="height: 500px; width: 100%"></div>
|
||||
</div>
|
||||
<script type="module" src="./main.ts"></script>
|
||||
|
170
packages/g6/tests/integration/animations-node-build-in.spec.ts
Normal file
@ -0,0 +1,170 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import nodeBuildIn from '../demo/animations/node-build-in';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe('Animation node buildIn', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = nodeBuildIn({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
const nodes = graph.getAllNodesData();
|
||||
|
||||
/**
|
||||
* Time: 0
|
||||
*/
|
||||
nodes.forEach(({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
node.animations.forEach((animation) => {
|
||||
animation.currentTime = 0;
|
||||
animation.pause();
|
||||
});
|
||||
});
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'animations-node-build-in-ready',
|
||||
);
|
||||
|
||||
/**
|
||||
* Time: 200
|
||||
*/
|
||||
nodes.forEach(({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
node.animations.forEach((animation) => {
|
||||
animation.currentTime = 200;
|
||||
animation.pause();
|
||||
});
|
||||
});
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'animations-node-build-in-running',
|
||||
);
|
||||
|
||||
/**
|
||||
* Resume all animations.
|
||||
*/
|
||||
nodes.forEach(({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
node.animations.forEach((animation) => {
|
||||
animation.play();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Time: finished
|
||||
*/
|
||||
await Promise.all(
|
||||
nodes.map(async ({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
await Promise.all(
|
||||
node.animations.map((animation) => animation.finished),
|
||||
);
|
||||
}),
|
||||
);
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'animations-node-build-in-finished',
|
||||
);
|
||||
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = nodeBuildIn({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
const nodes = graph.getAllNodesData();
|
||||
|
||||
/**
|
||||
* Time: 0
|
||||
*/
|
||||
nodes.forEach(({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
node.animations.forEach((animation) => {
|
||||
animation.currentTime = 0;
|
||||
animation.pause();
|
||||
});
|
||||
});
|
||||
await expect(canvas).toMatchSVGSnapshot(
|
||||
dir,
|
||||
'animations-node-build-in-ready',
|
||||
);
|
||||
|
||||
/**
|
||||
* Time: 200
|
||||
*/
|
||||
nodes.forEach(({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
node.animations.forEach((animation) => {
|
||||
animation.currentTime = 200;
|
||||
animation.pause();
|
||||
});
|
||||
});
|
||||
await expect(canvas).toMatchSVGSnapshot(
|
||||
dir,
|
||||
'animations-node-build-in-running',
|
||||
);
|
||||
|
||||
/**
|
||||
* Resume all animations.
|
||||
*/
|
||||
nodes.forEach(({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
node.animations.forEach((animation) => {
|
||||
animation.play();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Time: finished
|
||||
*/
|
||||
await Promise.all(
|
||||
nodes.map(async ({ id }) => {
|
||||
const node = graph['getItemById'](id);
|
||||
await Promise.all(
|
||||
node.animations.map((animation) => animation.finished),
|
||||
);
|
||||
}),
|
||||
);
|
||||
await expect(canvas).toMatchSVGSnapshot(
|
||||
dir,
|
||||
'animations-node-build-in-finished',
|
||||
);
|
||||
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,82 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import activateRelations from '../demo/behaviors/activate-relations';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext, triggerEvent } from './utils';
|
||||
|
||||
describe('Activate relations behavior', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = activateRelations({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'behaviors-activate-relations',
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
// mouseEvent.target = canvas.getContextService().getDomElement();
|
||||
triggerEvent(graph, 'mousedown', 81, 50);
|
||||
triggerEvent(graph, 'mouseup', 81, 50);
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'behaviors-activate-relations-activate-node1',
|
||||
);
|
||||
|
||||
/**
|
||||
* Click document to clear active state.
|
||||
*/
|
||||
triggerEvent(graph, 'mousedown', 0, 0);
|
||||
triggerEvent(graph, 'mouseup', 0, 0);
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'behaviors-activate-relations-deactivate-node1',
|
||||
);
|
||||
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = activateRelations({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchSVGSnapshot(
|
||||
dir,
|
||||
'behaviors-activate-relations',
|
||||
);
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
136
packages/g6/tests/integration/items-edge-line.spec.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import lineEdge from '../demo/item/edge/line-edge';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe('Items edge line', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = lineEdge({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(dir, 'items-edge-line');
|
||||
|
||||
/**
|
||||
* Click the checkbox to show label.
|
||||
*/
|
||||
const $showLabel = document.querySelectorAll(
|
||||
'input',
|
||||
)[0] as HTMLInputElement;
|
||||
$showLabel.click();
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'items-edge-line-show-label',
|
||||
);
|
||||
$showLabel.click();
|
||||
|
||||
/**
|
||||
* Click the checkbox to display selected style.
|
||||
*/
|
||||
const $selected = document.querySelectorAll(
|
||||
'input',
|
||||
)[2] as HTMLInputElement;
|
||||
$selected.click();
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'items-edge-line-selected-style',
|
||||
);
|
||||
$selected.click();
|
||||
|
||||
/**
|
||||
* Click the checkbox to highlight.
|
||||
*/
|
||||
const $highlight = document.querySelectorAll(
|
||||
'input',
|
||||
)[3] as HTMLInputElement;
|
||||
$highlight.click();
|
||||
await expect(canvas).toMatchCanvasSnapshot(
|
||||
dir,
|
||||
'items-edge-line-highlight-style',
|
||||
);
|
||||
$highlight.click();
|
||||
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = lineEdge({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchSVGSnapshot(dir, 'items-edge-line');
|
||||
|
||||
/**
|
||||
* Click the checkbox to show label.
|
||||
*/
|
||||
const $showLabel = document.querySelectorAll(
|
||||
'input',
|
||||
)[0] as HTMLInputElement;
|
||||
$showLabel.click();
|
||||
await expect(canvas).toMatchSVGSnapshot(
|
||||
dir,
|
||||
'items-edge-line-show-label',
|
||||
);
|
||||
$showLabel.click();
|
||||
|
||||
/**
|
||||
* Click the checkbox to display selected style.
|
||||
*/
|
||||
const $selected = document.querySelectorAll(
|
||||
'input',
|
||||
)[2] as HTMLInputElement;
|
||||
$selected.click();
|
||||
await expect(canvas).toMatchSVGSnapshot(
|
||||
dir,
|
||||
'items-edge-line-selected-style',
|
||||
);
|
||||
$selected.click();
|
||||
|
||||
/**
|
||||
* Click the checkbox to highlight.
|
||||
*/
|
||||
const $highlight = document.querySelectorAll(
|
||||
'input',
|
||||
)[3] as HTMLInputElement;
|
||||
$highlight.click();
|
||||
await expect(canvas).toMatchSVGSnapshot(
|
||||
dir,
|
||||
'items-edge-line-highlight-style',
|
||||
);
|
||||
$highlight.click();
|
||||
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
76
packages/g6/tests/integration/layouts-circular.spec.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import circular from '../demo/layouts/circular';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe('Circular layout', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = circular({
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(dir, 'layouts-circular');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = circular({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchSVGSnapshot(dir, 'layouts-circular');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should be rendered correctly with WebGL', (done) => {
|
||||
const dir = `${__dirname}/snapshots/webgl`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('webgl', 500, 500);
|
||||
|
||||
const graph = circular({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchWebGLSnapshot(dir, 'layouts-circular');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
83
packages/g6/tests/integration/layouts-d3force.spec.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import d3force from '../demo/layouts/d3force';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe('D3Force layout', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = d3force({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(dir, 'layouts-d3force');
|
||||
|
||||
// const layoutAnimation = graph.getLayoutCurrentAnimation()!;
|
||||
// layoutAnimation.
|
||||
// layoutAnimation.currentTime = 0;
|
||||
// await expect(canvas).toMatchCanvasSnapshot(dir, 'layouts-d3force-0');
|
||||
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = d3force({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchSVGSnapshot(dir, 'layouts-d3force');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should be rendered correctly with WebGL', (done) => {
|
||||
const dir = `${__dirname}/snapshots/webgl`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('webgl', 500, 500);
|
||||
|
||||
const graph = d3force({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchWebGLSnapshot(dir, 'layouts-d3force');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
77
packages/g6/tests/integration/layouts-dagre.spec.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import dagre from '../demo/layouts/dagre';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe('Dagre layout', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = dagre({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(dir, 'layouts-dagre');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = dagre({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchSVGSnapshot(dir, 'layouts-dagre');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should be rendered correctly with WebGL', (done) => {
|
||||
const dir = `${__dirname}/snapshots/webgl`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('webgl', 500, 500);
|
||||
|
||||
const graph = dagre({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchWebGLSnapshot(dir, 'layouts-dagre');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
77
packages/g6/tests/integration/layouts-force.spec.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import force from '../demo/layouts/force';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe.skip('Force layout', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = force({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(dir, 'layouts-force');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = force({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchSVGSnapshot(dir, 'layouts-force');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should be rendered correctly with WebGL', (done) => {
|
||||
const dir = `${__dirname}/snapshots/webgl`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('webgl', 500, 500);
|
||||
|
||||
const graph = force({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchWebGLSnapshot(dir, 'layouts-force');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
36
packages/g6/tests/integration/layouts-force3d.spec.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import force3D from '../demo/layouts/force-3d';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe('Force3D layout', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it.skip('should be rendered correctly with WebGL', (done) => {
|
||||
const dir = `${__dirname}/snapshots/webgl`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('webgl', 500, 500);
|
||||
|
||||
const graph = force3D({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
renderer: 'webgl-3d',
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchWebGLSnapshot(dir, 'layouts-force3d');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
77
packages/g6/tests/integration/layouts-grid.spec.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { resetEntityCounter } from '@antv/g';
|
||||
import grid from '../demo/layouts/grid';
|
||||
import './utils/useSnapshotMatchers';
|
||||
import { createContext } from './utils';
|
||||
|
||||
describe('Grid layout', () => {
|
||||
beforeEach(() => {
|
||||
/**
|
||||
* SVG Snapshot testing will generate a unique id for each element.
|
||||
* Reset to 0 to keep snapshot consistent.
|
||||
*/
|
||||
resetEntityCounter();
|
||||
});
|
||||
|
||||
it('should be rendered correctly with Canvas2D', (done) => {
|
||||
const dir = `${__dirname}/snapshots/canvas`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('canvas', 500, 500);
|
||||
|
||||
const graph = grid({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchCanvasSnapshot(dir, 'layouts-grid');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be rendered correctly with SVG', (done) => {
|
||||
const dir = `${__dirname}/snapshots/svg`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('svg', 500, 500);
|
||||
|
||||
const graph = grid({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchSVGSnapshot(dir, 'layouts-grid');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should be rendered correctly with WebGL', (done) => {
|
||||
const dir = `${__dirname}/snapshots/webgl`;
|
||||
const { backgroundCanvas, canvas, transientCanvas, container } =
|
||||
createContext('webgl', 500, 500);
|
||||
|
||||
const graph = grid({
|
||||
container,
|
||||
backgroundCanvas,
|
||||
canvas,
|
||||
transientCanvas,
|
||||
width: 500,
|
||||
height: 500,
|
||||
});
|
||||
|
||||
graph.on('afterlayout', async () => {
|
||||
await expect(canvas).toMatchWebGLSnapshot(dir, 'layouts-grid');
|
||||
graph.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 23 KiB |
BIN
packages/g6/tests/integration/snapshots/canvas/layouts-dagre.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
packages/g6/tests/integration/snapshots/canvas/layouts-grid.png
Normal file
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 34 KiB |