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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
JAVA_VERSION: 21

jobs:
test:
strategy:
fail-fast: false
matrix:
node-version: ['22.x']
os: [ubuntu-latest, windows-2022, macos-latest]

runs-on: ${{ matrix.os }}

steps:
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://registry.npmjs.org/'

- name: Checkout repository
uses: actions/checkout@v4

- name: Install dependencies
run: npm install

- name: Build the project
run: npm run build

- name: Run tests
run: npm run test
10 changes: 1 addition & 9 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { weaverConfig } from "@specs-feup/clava/code/WeaverConfiguration.js";

const config = {
preset: "ts-jest/presets/default-esm",
testEnvironment: "@specs-feup/lara/jest/jestEnvironment.js",
testEnvironmentOptions: {
weaverConfig,
},
globalSetup: "@specs-feup/lara/jest/jestGlobalSetup.js",
globalTeardown: "@specs-feup/lara/jest/jestGlobalTeardown.js",
setupFiles: ["@specs-feup/lara/jest/setupFiles/sharedJavaModule.js"],
testEnvironment: "node",
//notify: true,
//notifyMode: "always",
//verbose: true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"test:watch": "npm run test -- --watch"
},
"dependencies": {
"@specs-feup/lara": "^3.0.4",
"@specs-feup/lara": "^3.5.2",
"express": "^4.21.2",
"ws": "^8.18.0"
},
Expand Down
60 changes: 60 additions & 0 deletions src/AstConverterUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
addIdentation,
escapeHtml,
getNodeCodeTags,
getSpanTags,
getSyntaxHighlightTags,
normalizeFilepath,
} from "./AstConverterUtils.js";

describe("AstConverterUtils", () => {
describe("addIdentation", () => {
it("indents every line except the first one", () => {
expect(addIdentation("first\nsecond\nthird", 2)).toBe(
"first\n second\n third"
);
});

it("leaves single-line code unchanged", () => {
expect(addIdentation("const value = 1;", 3)).toBe("const value = 1;");
});
});

it("escapes HTML control characters without changing other text", () => {
expect(escapeHtml("if (left < right && right > 0)"))
.toBe("if (left &lt; right &amp;&amp; right &gt; 0)");
});

it("creates generic span tags with all supplied attributes", () => {
expect(getSpanTags('class="node"', 'data-id="42"')).toEqual([
'<span class="node" data-id="42">',
"</span>",
]);
});

it("creates node-code span tags", () => {
expect(getNodeCodeTags("node-7")).toEqual([
'<span class="node-code" data-node-id="node-7">',
"</span>",
]);
});

it.each(["comment", "keyword", "literal", "string", "type"] as const)(
"creates %s syntax-highlighting tags",
(type) => {
expect(getSyntaxHighlightTags(type)).toEqual([
`<span class="${type}">`,
"</span>",
]);
}
);

it("normalizes Windows separators and preserves POSIX separators", () => {
expect(normalizeFilepath("src\\public\\index.html")).toBe(
"src/public/index.html"
);
expect(normalizeFilepath("src/public/index.html")).toBe(
"src/public/index.html"
);
});
});
69 changes: 69 additions & 0 deletions src/public/js/ToolJoinPoint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import ToolJoinPoint from "./ToolJoinPoint.js";

describe("ToolJoinPoint", () => {
const jsonTree = {
id: "root",
type: "program",
code: "const answer = 42;",
filepath: "src/example.ts",
info: { language: "TypeScript" },
children: [
{
id: "child",
type: "declaration",
code: "const answer = 42;",
filepath: "src/example.ts",
info: { name: "answer" },
children: [],
},
],
};

it("builds a nested join-point tree from JSON", () => {
const root = ToolJoinPoint.fromJson(jsonTree);

expect(root.id).toBe("root");
expect(root.type).toBe("program");
expect(root.code).toBe("const answer = 42;");
expect(root.filepath).toBe("src/example.ts");
expect(root.info).toEqual({ language: "TypeScript" });
expect(root.children).toHaveLength(1);
expect(root.children[0]).toBeInstanceOf(ToolJoinPoint);
expect(root.children[0].id).toBe("child");
});

it("serializes a nested join-point tree to JSON", () => {
expect(ToolJoinPoint.fromJson(jsonTree).toJson()).toEqual(jsonTree);
});

it("clones the complete tree into new join-point instances", () => {
const original = ToolJoinPoint.fromJson(jsonTree);
const clone = original.clone();

expect(clone).not.toBe(original);
expect(clone.children[0]).not.toBe(original.children[0]);
expect(clone.toJson()).toEqual(original.toJson());
});

it("supports join points without code or a filepath", () => {
const joinPoint = new ToolJoinPoint(
"synthetic",
"scope",
undefined,
undefined,
{},
[]
);

expect(joinPoint.code).toBeUndefined();
expect(joinPoint.filepath).toBeUndefined();
expect(joinPoint.toJson()).toEqual({
id: "synthetic",
type: "scope",
code: undefined,
filepath: undefined,
info: {},
children: [],
});
});
});
15 changes: 15 additions & 0 deletions src/public/js/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { countChar } from "./utils.js";

describe("countChar", () => {
it("counts every occurrence of the requested character", () => {
expect(countChar("abracadabra", "a")).toBe(5);
});

it("returns zero when the character is absent", () => {
expect(countChar("visualization", "x")).toBe(0);
});

it("handles Unicode characters", () => {
expect(countChar("a✨b✨c", "✨")).toBe(2);
});
});
Loading