diff --git a/plugins/code-coverage/dev/__fixtures__/coverage-for-entity.json b/plugins/code-coverage/dev/__fixtures__/coverage-for-entity.json
new file mode 100644
index 0000000000..c2f4725818
--- /dev/null
+++ b/plugins/code-coverage/dev/__fixtures__/coverage-for-entity.json
@@ -0,0 +1,174 @@
+{
+ "metadata": {
+ "vcs": {
+ "type": "github",
+ "location": "https://github.com/backstage/backstage/tree/main/"
+ },
+ "generationTime": 1694413579498
+ },
+ "entity": {
+ "name": "backstage",
+ "namespace": "default",
+ "kind": "Component"
+ },
+ "files": [
+ {
+ "filename": "src/index.js",
+ "lineHits": {
+ "1": 1,
+ "2": 1,
+ "3": 1,
+ "4": 13,
+ "5": 13,
+ "6": 3,
+ "7": 13,
+ "8": 3,
+ "9": 13,
+ "10": 3,
+ "11": 13,
+ "12": 3,
+ "13": 13,
+ "14": 1,
+ "15": 13,
+ "16": 13
+ },
+ "branchHits": {
+ "3": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "5": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "7": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "9": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "11": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "13": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ }
+ }
+ },
+ {
+ "filename": "src/math.js",
+ "lineHits": {
+ "1": 1,
+ "2": 3,
+ "3": 2,
+ "4": 2,
+ "5": 1,
+ "6": 1,
+ "7": 1,
+ "8": 1,
+ "9": 3,
+ "10": 2,
+ "11": 2,
+ "12": 1,
+ "13": 1,
+ "14": 1,
+ "15": 1,
+ "16": 3,
+ "17": 2,
+ "18": 2,
+ "19": 1,
+ "20": 1,
+ "21": 1,
+ "22": 1,
+ "23": 3,
+ "24": 2,
+ "25": 3,
+ "26": 0,
+ "27": 0,
+ "28": 1,
+ "29": 1
+ },
+ "branchHits": {
+ "2": {
+ "covered": 3,
+ "available": 3,
+ "missed": 0
+ },
+ "5": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "8": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "9": {
+ "covered": 2,
+ "available": 2,
+ "missed": 0
+ },
+ "12": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "15": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "16": {
+ "covered": 2,
+ "available": 2,
+ "missed": 0
+ },
+ "19": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "22": {
+ "covered": 1,
+ "available": 1,
+ "missed": 0
+ },
+ "23": {
+ "covered": 2,
+ "available": 2,
+ "missed": 0
+ },
+ "25": {
+ "covered": 1,
+ "available": 2,
+ "missed": 1
+ }
+ }
+ }
+ ],
+ "aggregate": {
+ "line": {
+ "available": 45,
+ "covered": 43,
+ "missed": 2,
+ "percentage": 95.56
+ },
+ "branch": {
+ "available": 23,
+ "covered": 22,
+ "missed": 1,
+ "percentage": 95.65
+ }
+ }
+}
diff --git a/plugins/code-coverage/dev/__fixtures__/coverage-history-for-entity.json b/plugins/code-coverage/dev/__fixtures__/coverage-history-for-entity.json
new file mode 100644
index 0000000000..26b96875e5
--- /dev/null
+++ b/plugins/code-coverage/dev/__fixtures__/coverage-history-for-entity.json
@@ -0,0 +1,84 @@
+{
+ "entity": {
+ "name": "backstage",
+ "kind": "component",
+ "namespace": "default"
+ },
+ "history": [
+ {
+ "timestamp": 1694413579498,
+ "branch": {
+ "available": 23,
+ "covered": 22,
+ "missed": 1,
+ "percentage": 95.65
+ },
+ "line": {
+ "available": 45,
+ "covered": 43,
+ "missed": 2,
+ "percentage": 95.56
+ }
+ },
+ {
+ "timestamp": 1694178927072,
+ "branch": {
+ "available": 23,
+ "covered": 22,
+ "missed": 1,
+ "percentage": 95.65
+ },
+ "line": {
+ "available": 45,
+ "covered": 43,
+ "missed": 2,
+ "percentage": 95.56
+ }
+ },
+ {
+ "timestamp": 1694178830777,
+ "branch": {
+ "available": 19,
+ "covered": 18,
+ "missed": 1,
+ "percentage": 94.74
+ },
+ "line": {
+ "available": 45,
+ "covered": 43,
+ "missed": 2,
+ "percentage": 95.56
+ }
+ },
+ {
+ "timestamp": 1694178708402,
+ "branch": {
+ "available": 15,
+ "covered": 10,
+ "missed": 5,
+ "percentage": 66.67
+ },
+ "line": {
+ "available": 45,
+ "covered": 36,
+ "missed": 9,
+ "percentage": 80
+ }
+ },
+ {
+ "timestamp": 1694178270486,
+ "branch": {
+ "available": 10,
+ "covered": 5,
+ "missed": 5,
+ "percentage": 50
+ },
+ "line": {
+ "available": 45,
+ "covered": 26,
+ "missed": 19,
+ "percentage": 57.78
+ }
+ }
+ ]
+}
diff --git a/plugins/code-coverage/dev/__fixtures__/file-content-index-js.txt b/plugins/code-coverage/dev/__fixtures__/file-content-index-js.txt
new file mode 100644
index 0000000000..34df5226d5
--- /dev/null
+++ b/plugins/code-coverage/dev/__fixtures__/file-content-index-js.txt
@@ -0,0 +1,16 @@
+import { add, multiply, subtract, divide } from "./math";
+
+export default (a, b, operator) => {
+ switch(operator) {
+ case '+':
+ return add(a, b);
+ case '-':
+ return subtract(a, b);
+ case '*':
+ return multiply(a, b);
+ case '/':
+ return divide(a, b);
+ default:
+ throw new Error('Invalid operator');
+ }
+};
diff --git a/plugins/code-coverage/dev/__fixtures__/file-content-math-js.txt b/plugins/code-coverage/dev/__fixtures__/file-content-math-js.txt
new file mode 100644
index 0000000000..05f43438f5
--- /dev/null
+++ b/plugins/code-coverage/dev/__fixtures__/file-content-math-js.txt
@@ -0,0 +1,29 @@
+export function add(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ }
+ return a + b;
+}
+
+export function subtract(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ }
+ return a - b;
+}
+
+export function multiply(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ }
+ return a * b;
+}
+
+export function divide(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ } else if (b === 0) {
+ throw new Error('Division by zero');
+ }
+ return a / b;
+}
diff --git a/plugins/code-coverage/dev/__fixtures__/get-file-content-from-entity.ts b/plugins/code-coverage/dev/__fixtures__/get-file-content-from-entity.ts
new file mode 100644
index 0000000000..9fea0997f7
--- /dev/null
+++ b/plugins/code-coverage/dev/__fixtures__/get-file-content-from-entity.ts
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export default {
+ 'src/index.js': `import { add, multiply, subtract, divide } from \"./math\";
+
+export default (a, b, operator) => {
+ switch(operator) {
+ case '+':
+ return add(a, b);
+ case '-':
+ return subtract(a, b);
+ case '*':
+ return multiply(a, b);
+ case '/':
+ return divide(a, b);
+ default:
+ throw new Error('Invalid operator');
+ }
+};
+ `,
+ 'src/math.js': `export function add(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ }
+ return a + b;
+}
+
+export function subtract(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ }
+ return a - b;
+}
+
+export function multiply(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ }
+ return a * b;
+}
+
+export function divide(a, b) {
+ if (isNaN(a) || isNaN(b)) {
+ throw new Error('Invalid input');
+ } else if (b === 0) {
+ throw new Error('Division by zero');
+ }
+ return a / b;
+}
+ `,
+};
diff --git a/plugins/code-coverage/dev/index.tsx b/plugins/code-coverage/dev/index.tsx
index f364c2a0d1..e0c1ef7616 100644
--- a/plugins/code-coverage/dev/index.tsx
+++ b/plugins/code-coverage/dev/index.tsx
@@ -16,11 +16,65 @@
import React from 'react';
import { createDevApp } from '@backstage/dev-utils';
import { codeCoveragePlugin, EntityCodeCoverageContent } from '../src/plugin';
+import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
+import { EntityProvider } from '@backstage/plugin-catalog-react';
+import { codeCoverageApiRef, CodeCoverageApi } from '../src/api';
+import coverageForEntity from './__fixtures__/coverage-for-entity.json';
+import coverageHistoryForEntity from './__fixtures__/coverage-history-for-entity.json';
+import fileContentFromEntity from './__fixtures__/get-file-content-from-entity';
+
+const mockEntity: Entity = {
+ apiVersion: 'backstage.io/v1alpha1',
+ kind: 'Component',
+ metadata: {
+ name: 'backstage',
+ description: 'backstage.io',
+ annotations: {
+ 'backstage.io/code-coverage': 'enabled',
+ },
+ },
+ spec: {
+ lifecycle: 'production',
+ type: 'website',
+ owner: 'user:guest',
+ },
+};
+
+const mockCodeCoverageApi: CodeCoverageApi = {
+ async getCoverageForEntity(_entity: CompoundEntityRef) {
+ return coverageForEntity as any;
+ },
+ async getFileContentFromEntity(_entity: CompoundEntityRef, filePath: string) {
+ switch (filePath) {
+ case 'src/index.js':
+ return fileContentFromEntity['src/index.js'];
+ case 'src/math.js':
+ return fileContentFromEntity['src/math.js'];
+ default:
+ return '';
+ }
+ },
+ async getCoverageHistoryForEntity(
+ _entity: CompoundEntityRef,
+ _limit?: number,
+ ) {
+ return coverageHistoryForEntity;
+ },
+};
createDevApp()
+ .registerApi({
+ api: codeCoverageApiRef,
+ deps: {},
+ factory: () => mockCodeCoverageApi,
+ })
.registerPlugin(codeCoveragePlugin)
.addPage({
- element: ,
+ element: (
+
+
+
+ ),
title: 'Root Page',
})
.render();