Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Code Quality Assurance
name: Code Quality Assurance - JavaScript

on:
push:
Expand All @@ -9,7 +9,7 @@ on:
- "**/*.js"
- "**/*.css"
- "**/*.html"
- ".github/workflows/code-qa.yaml"
- ".github/workflows/code-qa-js.yaml"
pull_request:
branches: [main]
paths:
Expand All @@ -18,7 +18,7 @@ on:
- "**/*.js"
- "**/*.css"
- "**/*.html"
- ".github/workflows/code-qa.yaml"
- ".github/workflows/code-qa-js.yaml"

permissions:
contents: read
Expand All @@ -35,7 +35,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Cache Node.js modules
Expand All @@ -45,7 +45,18 @@ jobs:
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

- name: Check installs
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps

- name: Quality check - prettier
run: npm run prettier:check
- name: Lint check - ESLint
run: npm run eslint:check

- name: Unit tests - jest
run: npm test:jest
Comment thread
AlexJSully marked this conversation as resolved.
Outdated
- name: E2E tests - Playwright
run: npm run test:playwright:headless
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Markdown Lint
name: Code Quality Assurance - Markdown

on:
push:
Expand All @@ -7,14 +7,14 @@ on:
- "**/*.md"
- ".markdownlint.json"
- ".markdownlintignore"
- ".github/workflows/markdown-lint.yml"
- ".github/workflows/code-qa-markdown.yml"
Comment thread
AlexJSully marked this conversation as resolved.
Outdated
pull_request:
branches: [main]
paths:
- "**/*.md"
- ".markdownlint.json"
- ".markdownlintignore"
- ".github/workflows/markdown-lint.yml"
- ".github/workflows/code-qa-markdown.yml"
Comment thread
AlexJSully marked this conversation as resolved.
Outdated

permissions:
contents: read
Expand All @@ -26,7 +26,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Cache Node.js modules
Expand Down
61 changes: 61 additions & 0 deletions .github/workflows/code-qa-python.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Code Quality Assurance - Python

on:
push:
branches: [main]
paths:
- "cgi-bin/**/*.cgi"
- "requirements*.txt"
- ".github/workflows/code-qa-python.yaml"
pull_request:
branches: [main]
paths:
- "cgi-bin/**/*.cgi"
- "requirements*.txt"
- ".github/workflows/code-qa-python.yaml"

permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: [3.13]

steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "latest"

- name: Cache Python packages
uses: actions/cache@v4
with:
path: |
~/.cache/uv
~/.cache/pip
key: ${{ runner.os }}-python-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
${{ runner.os }}-python-

- name: Create virtual environment
run: uv venv

- name: Install dependencies
run: make py-install

- name: Format check - Ruff
run: uv run ruff format cgi-bin/*.cgi

- name: Lint check - Ruff
run: uv run ruff check --fix cgi-bin/*.cgi

- name: Run Python tests
run: uv run pytest
6 changes: 3 additions & 3 deletions .github/workflows/r.yaml → .github/workflows/code-qa-r.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: R Quality Assurance
name: Code Quality Assurance - R

on:
push:
Expand All @@ -7,14 +7,14 @@ on:
paths:
- "**/*.Rmd"
- "**/*.R"
- ".github/workflows/r.yaml"
- ".github/workflows/code-qa-r.yaml"
pull_request:
branches:
- main
paths:
- "**/*.Rmd"
- "**/*.R"
- ".github/workflows/r.yaml"
- ".github/workflows/code-qa-r.yaml"

permissions:
contents: read
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -72,7 +72,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
Expand All @@ -85,6 +85,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,18 @@ renv/.gitignore

# Python virtual environment
venv/
__pycache__/
.mypy_cache/
.pytest_cache/
.ruff_cache/
.venv/

# Test output
coverage/

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
18 changes: 18 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ cgi-bin/core/serviceWorker
# Ignore minified files
*.min.*



# R renv library
renv/library
renv/local
Expand All @@ -60,6 +62,22 @@ renv/lock
renv/python
renv/sandbox
renv/staging
renv/.gitignore

# Python virtual environment
venv/
__pycache__/
.mypy_cache/
.pytest_cache/
.ruff_cache/
.venv/

# Test output
coverage/

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Validate all
.PHONY: validate-all
validate-all: py-validate
# Validate JavaScript/TypeScript
# If node_modules is missing, run 'npm install' first
@if [ ! -d node_modules ]; then npm ci; fi
npm run validate
# Validate R installation
Rscript -e 'renv::restore()'

# === Python Commands for eFP-Seq_Browser ===
.PHONY: py-install py-format py-lint py-test py-clean py-validate

PY_FILES = cgi-bin/*.cgi

py-install:
uv pip install -r requirements-dev.txt

py-format:
uv run ruff format $(PY_FILES)

py-lint:
uv run ruff check --fix $(PY_FILES)

py-validate: py-install py-format py-lint
94 changes: 94 additions & 0 deletions cgi-bin/Submission_page/XMLgenerator.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* @jest-environment jsdom
*/

const fs = require("fs");
const path = require("path");

describe("update (XMLgenerator.js)", () => {
let update;
let $;

beforeAll(() => {
// Minimal jQuery mock
$ = () => ({
find: (selector) => ({
val: () => {
const map = {
".channelcontrols": "ctrl1, ctrl2",
".channelgroupwidtho": "rep1, rep2",
".channeldescription": "desc",
".channelrecordnumber": "42",
".channelhexcolor": "#ff0000",
".channelbamType": "Google Drive",
".channelbamlink": "https://drive.google.com/file/d/abc",
".channeltotalreadsmapped": "12345",
".channelreadmapmethod": "STAR",
".channelpublicationlink": "https://pub.com/xyz",
".channeltissue": "Leaf",
".channelsvgname": "ath-leaf.svg",
".channeltitle": "Test Title",
".channelsralink": "SRR000001",
".channelspecies": "Arabidopsis",
".channelforeground": "#000000",
".channelfilename": "file1.bam",
};
return map[selector] || "dummy";
},
}),
});

// Robust document.getElementById mock for all ids
const getElementByIdMock = (id) => {
const values = {
reqxml: { value: "TestXML" },
reqauthor: { value: "Author" },
contectinfo: { value: "contact@email.com" },
};
return values[id] || { value: "dummy" };
};
if (global.document) {
global.document.getElementById = getElementByIdMock;
} else {
global.document = { getElementById: getElementByIdMock };
}
global.$ = $;

// Required top-level variables for update()
global.topXML = [
'\t\t<file info="<?channeldescription?>" record_number="<?channelrecordnumber?>" foreground="<?channelforeground?>" hex_colour="<?channelhexcolor?>" bam_type="<?channelbamType?>" name="<?channelbamlink?>" filename="<?channelfilename?>" total_reads_mapped="<?channeltotalreadsmapped?>" read_map_method="<?channelreadmapmethod?>" publication_link="<?channelpublicationlink?>" svg_subunit="<?channeltissue?>" svgname="<?channelsvgname?>" description="<?channeltitle?>" url="<?channelsralink?>" species="<?channelspecies?>" title="<?channeligbtitle?>">',
"\t\t\t<controls>\n",
].join("\r\n");
global.controlsXML = "";
global.replicatesXML = ["\t\t\t</controls>", "\t\t\t<groupwith>\n"].join("\r\n");
global.endingXML = ["\t\t\t</groupwith>", "\t\t</file>", "\n"].join("\r\n");
global.existingXML = "";
global.all_controls = "";
global.all_replicates = "";

// Load the update function from XMLgenerator.js
const code = fs.readFileSync(path.join(__dirname, "../Submission_page/XMLgenerator.js"), "utf8");
const fnMatch = code.match(/function update\s*\(([^)]*)\)\s*{([\s\S]*?)^}/m);
if (!fnMatch) throw new Error("update function not found in XMLgenerator.js");
const args = fnMatch[1];
const body = fnMatch[2];

update = new Function(args, body);
});

it("generates correct XML for given form values", () => {
const v = {}; // dummy, not used in our $ mock
const result = update("", v);
// Check for key XML elements and values
expect(result).toContain('info="desc"');
expect(result).toContain('record_number="42"');
expect(result).toContain('hex_colour="#ff0000"');
expect(result).toContain('bam_type="Google Drive"');
expect(result).toContain('name="https://drive.google.com/file/d/abc"');
expect(result).toContain('filename="file1.bam"');
expect(result).toContain("<bam_exp>ctrl1</bam_exp>");
expect(result).toContain("<bam_exp>ctrl2</bam_exp>");
expect(result).toContain("<bam_exp>rep1</bam_exp>");
expect(result).toContain("<bam_exp>rep2</bam_exp>");
});
});
5 changes: 2 additions & 3 deletions cgi-bin/core/custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ let loadNewDataset = false;

/** Used to count and determine how many BAM entries are in the XML file */
let count_bam_entries_in_xml = 113;
/**
* Count the amount of entries in a BAM file
*/

/** Count the amount of entries in a BAM file */
function count_bam_num() {
const xhr = new XMLHttpRequest();
const url = base_src;
Expand Down
Loading
Loading