The DNA sequence alignment feature implements the CRISPResso2 Needleman-Wunsch global alignment algorithm with affine gap penalties. This guide provides comprehensive test cases to verify that the JavaScript implementation (js/alignment.js) matches the behavior of the original Cython code (crispresso_align.pyx).
Recommended for frequent testing:
# Run automated command-line tests
npm test
# Or run directly with Node.js
node alignment.test.jsThe automated test suite (alignment.test.js) runs all test cases and provides:
- ✓ Color-coded pass/fail output
- Detailed error messages with expected vs actual values
- Summary statistics
- Exit code 0 on success, 1 on failure (CI/CD friendly)
No installation required - uses only Node.js built-in modules (no external dependencies).
For visual inspection and debugging:
# Start local server
python -m http.server 8000
# Open in browser
http://localhost:8000/test-alignment.htmlThe HTML test page provides visual output showing aligned sequences with syntax highlighting.
- Match Score: 5 (from EDNAFULL matrix)
- Mismatch Score: -4
- N-Mismatch Score: -2 (N vs A/T/C/G)
- N-Match Score: -1 (N vs N)
- Gap Open Penalty: -1
- Gap Extend Penalty: -1
The algorithm uses three scoring matrices:
- M array: Best alignment ending with a match/mismatch
- I array: Best alignment ending with a gap in read (insertion in reference)
- J array: Best alignment ending with a gap in reference (deletion in reference)
Purpose: Verify perfect alignment with no gaps
Input:
- Reference (seqI):
ATCGATCG - Query (seqJ):
ATCGATCG - Gap Incentive: All zeros (length 9)
- Gap Open: -1
- Gap Extend: -1
Expected Output:
- Aligned Reference:
ATCGATCG - Aligned Query:
ATCGATCG - Match Percentage: 100.0%
- No gaps in either sequence
Test URL:
http://localhost:8000/#seq1=ATCGATCG&seq2=ATCGATCG
Purpose: Verify mismatch scoring
Input:
- Reference:
ATCGATCG - Query:
ATCTATCG
Expected Output:
- Aligned Reference:
ATCGATCG - Aligned Query:
ATCTATCG - Match Percentage: 87.5% (7 matches / 8 bases)
- Mismatch at position 3 (G vs T)
Test URL:
http://localhost:8000/#seq1=ATCGATCG&seq2=ATCTATCG
Purpose: Verify insertion handling (gap in reference)
Input:
- Reference:
ATCGATCG - Query:
ATCGAATCG
Expected Output:
- Aligned Reference:
ATCG-ATCG - Aligned Query:
ATCGAATCG - Gap in reference at position 4
- One base insertion (A) in query
Expected Match Percentage: ~88.9% (8 matches / 9 positions)
Test URL:
http://localhost:8000/#seq1=ATCGATCG&seq2=ATCGAATCG
Purpose: Verify deletion handling (gap in query)
Input:
- Reference:
ATCGATCG - Query:
ATCGTCG
Expected Output:
- Aligned Reference:
ATCGATCG - Aligned Query:
ATCG-TCG - Gap in query at position 4
- One base deletion (A) in query
Expected Match Percentage: ~87.5% (7 matches / 8 positions)
Test URL:
http://localhost:8000/#seq1=ATCGATCG&seq2=ATCGTCG
Purpose: Verify multiple gap handling
Input:
- Reference:
ATCG - Query:
AATTCCGG
Expected Output:
- Aligned Reference:
A-T-C-G-or similar with gaps - Aligned Query:
AATTCCGG - Multiple gaps in reference
- Query has double bases compared to reference
Expected: Multiple gaps distributed optimally according to scoring
Test URL:
http://localhost:8000/#seq1=ATCG&seq2=AATTCCGG
Purpose: Verify gap extension penalty
Input:
- Reference:
ATCGATCGATCG - Query:
ATCGCG
Expected Output:
- Aligned Reference:
ATCGATCGATCG - Aligned Query:
ATCG------CGor similar - Consecutive gaps should be grouped together (gap extension is cheaper than opening multiple gaps)
Expected: One contiguous gap region, not scattered gaps
Test URL:
http://localhost:8000/#seq1=ATCGATCGATCG&seq2=ATCGCG
Purpose: Verify no gap penalty at sequence start
Input:
- Reference:
ATCGATCG - Query:
GATCG
Expected Output:
- Aligned Reference:
ATCGATCG - Aligned Query:
---GATCGorATCGATCG(depends on best alignment) - Terminal gaps at beginning treated specially
Test URL:
http://localhost:8000/#seq1=ATCGATCG&seq2=GATCG
Purpose: Verify no gap opening penalty at sequence end
Input:
- Reference:
ATCGATCG - Query:
ATCGA
Expected Output:
- Aligned Reference:
ATCGATCG - Aligned Query:
ATCGA---or aligned without terminal gap penalty - Terminal gaps at end treated with only extension penalty
Test URL:
http://localhost:8000/#seq1=ATCGATCG&seq2=ATCGA
Purpose: Verify N base scoring
Input:
- Reference:
ATCGATCG - Query:
ATCNATCG
Expected Output:
- Aligned Reference:
ATCGATCG - Aligned Query:
ATCNATCG - N at position 3 should align with G
- Penalty: -2 (N-mismatch score)
Expected Match Percentage: Lower than perfect match but higher than complete mismatch
Test URL:
http://localhost:8000/#seq1=ATCGATCG&seq2=ATCNATCG
Purpose: Verify complex real-world scenario
Input:
- Reference:
ATCGATCGATCGATCGATCG - Query:
ATCGATTCGATCGAATCG
Expected Output:
- Multiple mismatches and potential gaps
- Algorithm should find optimal alignment
- Aligned sequences should be same length
Test URL:
http://localhost:8000/#seq1=ATCGATCGATCGATCGATCG&seq2=ATCGATTCGATCGAATCG
Purpose: Edge case - minimal sequences
Input:
- Reference:
AT - Query:
AT
Expected Output:
- Aligned Reference:
AT - Aligned Query:
AT - Match Percentage: 100.0%
Test URL:
http://localhost:8000/#seq1=AT&seq2=AT
Purpose: Verify behavior with no similarity
Input:
- Reference:
AAAAAAAAAA - Query:
TTTTTTTTTT
Expected Output:
- Aligned Reference:
AAAAAAAAAA - Aligned Query:
TTTTTTTTTT - Match Percentage: 0.0%
- All positions are mismatches
Test URL:
http://localhost:8000/#seq1=AAAAAAAAAA&seq2=TTTTTTTTTT
Purpose: Verify alignment when query is much longer
Input:
- Reference:
ATCG - Query:
ATCGATCGATCGATCG
Expected Output:
- Aligned sequences same length
- Gaps in reference to accommodate longer query
- Optimal alignment found
Test URL:
http://localhost:8000/#seq1=ATCG&seq2=ATCGATCGATCGATCG
Purpose: Verify alignment when reference is much longer
Input:
- Reference:
ATCGATCGATCGATCG - Query:
ATCG
Expected Output:
- Aligned sequences same length
- Gaps in query to accommodate longer reference
- Optimal alignment found
Test URL:
http://localhost:8000/#seq1=ATCGATCGATCGATCG&seq2=ATCG
Purpose: Simulate actual CRISPR deletion
Input:
- Reference:
ATCGATCGATCGCCCCCCCCCCCCCCCCCCCCCATCGATCGATCG - Query:
ATCGATCGATCGCCCCCCATCGATCGATCG(20bp deletion in middle)
Expected Output:
- Large gap region in query sequence
- Deletion from position ~12-32
- Flanking regions should align perfectly
Test URL:
http://localhost:8000/#seq1=ATCGATCGATCGCCCCCCCCCCCCCCCCCCCCCATCGATCGATCG&seq2=ATCGATCGATCGCCCCCCATCGATCGATCG
Purpose: Simulate actual CRISPR insertion
Input:
- Reference:
ATCGATCGATCGATCGATCG - Query:
ATCGATCGATCGGGGGGGGGGGGGGATCGATCG(12bp insertion in middle)
Expected Output:
- Large gap region in reference sequence
- Insertion around position ~12
- Flanking regions should align perfectly
Test URL:
http://localhost:8000/#seq1=ATCGATCGATCGATCGATCG&seq2=ATCGATCGATCGGGGGGGGGGGGGGATCGATCG
- Both aligned sequences must have exactly the same length
- Length = original length + gaps added
- All gaps represented by
-character - Gaps never appear in both sequences at same position
matchPercentage = (matchCount / alignmentLength) * 100
- Should be rounded to 3 decimal places
- Must match Cython implementation
The scoring matrix must match EDNAFULL:
A-A: 5 A-T: -4 A-C: -4 A-G: -4
T-T: 5 T-A: -4 T-C: -4 T-G: -4
C-C: 5 C-A: -4 C-T: -4 C-G: -4
G-G: 5 G-A: -4 G-T: -4 G-C: -4
N-N: -1 N-X: -2 (where X = A/T/C/G)- Gap Open: -1 (penalty for starting a gap)
- Gap Extend: -1 (penalty for extending existing gap)
- Last row/column: Only extension penalty (no opening penalty)
- Must start from bottom-right corner of matrix (maxI, maxJ)
- Must end at top-left corner (0, 0)
- Must follow pointer array (MARRAY, IARRAY, or JARRAY)
- Start local server:
python -m http.server 8000 - Open test URL in browser
- Click "Align Sequences" button
- Verify aligned sequences in textareas
- Check match percentage in console
Open browser console (F12) to see:
Alignment complete: {alignedSeqI: "...", alignedSeqJ: "...", matchPercentage: ...}
Match percentage: XX.XXX%
To create automated tests:
- Compare JavaScript output with Cython output for all test cases
- Use Python script to run Cython version and generate expected results
- Create Jest/Mocha tests to verify JavaScript matches expected output
- M[0,0] = 0 (starting point)
- M[0,j] = minScore for j > 0 (can't start with match in empty reference)
- M[i,0] = minScore for i > 0 (can't start with match in empty query)
- I[0,j] = gap_extend * j + gap_incentive[0] (gaps at start)
- J[i,0] = gap_extend * i + gap_incentive[0] (gaps at start)
- Fill interior cells (1 to maxI-1, 1 to maxJ-1)
- Fill last column (j = maxJ) with only gap extend penalty
- Fill last row (i = maxI) with only gap extend penalty
while i > 0 or j > 0:
if currMatrix == MARRAY:
- Add both bases to alignment
- Move diagonally (i-1, j-1)
elif currMatrix == JARRAY:
- Add gap to query, base to reference
- Move up (i-1, j)
elif currMatrix == IARRAY:
- Add base to query, gap to reference
- Move left (i, j-1)
- Check: Are scores being calculated correctly?
- Check: Is the matrix initialization correct?
- Check: Is traceback following the right pointers?
- Debug: Add console.log in scoring loop to print matrix values
- Check: Are you counting matches correctly during traceback?
- Check: Are you dividing by total alignment length (not original length)?
- Check: Are you rounding to 3 decimal places?
- Check: Is gap opening vs extension penalty correct?
- Check: Are last row/column using only extension penalty?
- Check: Is gap incentive array correct length (maxI + 1)?
- Check: Are both sequences being built during traceback?
- Check: Are gaps being added to correct sequence?
- Check: Is traceback stopping at (0,0)?
To verify JavaScript matches Cython, run both implementations on the same test cases:
import crispresso_align as cra
import numpy as np
# Create scoring matrix
matrix = cra.make_matrix(5, -4, -2, -1)
# Create gap incentive array (all zeros for uniform)
seq1 = "ATCGATCG"
gap_incentive = np.zeros(len(seq1) + 1, dtype=np.int32)
# Run alignment
seq2 = "ATCTATCG"
align_j, align_i, score = cra.global_align(seq2, seq1, matrix, gap_incentive, -1, -1)
print(f"Reference: {align_i}")
print(f"Query: {align_j}")
print(f"Score: {score}%")Run this for each test case and compare with JavaScript output.
- Create HTML test page with all test cases
- Button to run all tests and show pass/fail
- Compare against expected results stored in JSON
- Test with very long sequences (>10,000 bases)
- Measure time to align
- Compare JavaScript vs Cython performance
- Create visualization of scoring matrices
- Show traceback path through matrices
- Highlight optimal alignment path
- Original Cython code:
crispresso_align.pyx - JavaScript implementation:
js/alignment.js - CRISPResso2 documentation: https://github.com/pinellolab/CRISPResso2
- EDNAFULL matrix:
EDNAFULLfile in repository - Needleman-Wunsch algorithm: https://en.wikipedia.org/wiki/Needleman%E2%80%93Wunsch_algorithm