Tell us what’s happening:
I am trying to work through testing my sudoku solver project. But, I am having trouble with the headless browser, JSDOM suggested in the boilerplate.
How do I simulate a user input with JSDOM, for the unit tests? I did the first two suites by adding check functions, but need to interact with the virtual DOM for the rest.
Should I use Zombie.js instead? Google search makes me think using its fill method might be better suited.
And also the challenges taught us a bit about Zombie, but nothing about JSDOM.
TL;DR - How can I simulate user input for testing? Zombie.js vs JSDOM, I know the prior is based on JSDOM but what is the practical difference related to testing?
Your code so far
sudoku-solver.js:
const textArea = document.getElementById('text-input');
const errorArea = document.getElementById('error-msg');
const solve = document.getElementById('solve-button');
const clear = document.getElementById('clear-button');
const cells = document.querySelectorAll('.sudoku-input');
import { puzzlesAndSolutions } from './puzzle-strings.js';
document.addEventListener('DOMContentLoaded', () => {
// Load a simple puzzle into the text area
textArea.value = '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
textChange();
});
let checkGridInput = (input) => {
var numberRE = /^[1-9]$|^[\0]$/;
return numberRE.test(input.toString());
}
let checkTextInput = (input) => {
var numberRE = /^[1-9.]*$/;
return numberRE.test(input.toString());
}
let textChange = () => {
errorArea.innerText = '';
var numberRE = /^[1-9.]*$/;
if(numberRE.test(textArea.value) === false) {
errorArea.innerText = "Error: Invalid character.";
return;
}
let text = textArea.value.split('');
if(text.length !== 81) {
errorArea.innerText = "Error: Expected puzzle to be 81 characters long.";
return;
}
text.forEach((ele, index) => {
if(ele === '.') {
ele = '';
}
cells[index].value = ele;
});
};
let gridChange = () => {
errorArea.innerText = '';
var numberRE = /^[1-9]$|^[\0]$/;
let textString = '';
cells.forEach((cell, index) => {
let cellValue = cell.value ? cell.value.toString() : '\0';
if(numberRE.test(cellValue)) {
if(cellValue == '\0') {
cellValue = '.';
}
textString += cellValue;
}
else {
errorArea.innerText = "Error: Invalid Grid character";
return;
}
});
//console.log(textString.length);
textArea.value = textString;
}
let solveSudoku = () => {
errorArea.innerText = '';
puzzlesAndSolutions.forEach((element, index) => {
if(textArea.value === element[0]) {
console.log("We have a match at: ", index);
textArea.value = element[1];
textChange();
return;
}
})
}
let clearAll = () => {
textArea.value = '';
cells.forEach((cell) => {
cell.value = '';
})
}
textArea.oninput = textChange;
cells.forEach((cell) => {
cell.oninput = gridChange;
})
solve.onclick = solveSudoku;
clear.onclick = clearAll;
/*
Export your functions for testing in Node.
Note: The `try` block is to prevent errors on
the client side
*/
try {
module.exports = {
checkGridInput: checkGridInput,
checkTextInput: checkTextInput,
textChange: textChange,
gridChange: gridChange,
solveSudoku: solveSudoku,
clear: clearAll
}
} catch (e) {}
tests.js:
const chai = require('chai');
const chaiHttp = require('chai-http');
const assert = chai.assert;
const server = require('../server');
chai.use(chaiHttp)
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
let Solver;
suite('UnitTests', () => {
suiteSetup(() => {
// Mock the DOM for testing and load Solver
return JSDOM.fromFile('./views/index.html')
.then((dom) => {
global.window = dom.window;
global.document = dom.window.document;
Solver = require('../public/sudoku-solver.js');
});
});
// Only the digits 1-9 are accepted
// as valid input for the puzzle grid
suite('Function checkGridInput()', () => {
test('Valid "1-9" characters', (done) => {
const input = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
input.forEach((ele) => {
assert.equal(Solver.checkGridInput(ele), true);
});
done();
});
// Invalid characters or numbers are not accepted
// as valid input for the puzzle grid
test('Invalid characters (anything other than "1-9") are not accepted', (done) => {
const input = ['!', 'a', '/', '+', '-', '0', '10', 0, '.'];
input.forEach((ele) => {
if(ele === '10') {
assert.equal(Solver.checkGridInput('1'), true);
}
else {
assert.equal(Solver.checkGridInput(ele), false);
}
});
done();
});
});
suite('Function checkTextInput()', () => {
test('Parses a valid puzzle string into an object', done => {
const input = '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
assert.equal(Solver.checkTextInput(input), true);
done();
});
// Puzzles that are not 81 numbers/periods long show the message
// "Error: Expected puzzle to be 81 characters long." in the
// `div` with the id "error-msg"
test('Shows an error for puzzles that are not 81 numbers long', done => {
const shortStr = '83.9.....6.62.71...9......1945....4.37.4.3..6..';
const longStr = '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6...';
const errorMsg = 'Error: Expected puzzle to be 81 characters long.';
const errorDiv = document.getElementById('error-msg');
done();
});
});
suite('Function ____()', () => {
// Valid complete puzzles pass
test('Valid puzzles pass', done => {
const input = '769235418851496372432178956174569283395842761628713549283657194516924837947381625';
// done();
});
// Invalid complete puzzles fail
test('Invalid puzzles fail', done => {
const input = '779235418851496372432178956174569283395842761628713549283657194516924837947381625';
// done();
});
});
suite('Function ____()', () => {
// Returns the expected solution for a valid, incomplete puzzle
test('Returns the expected solution for an incomplete puzzle', done => {
const input = '..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..';
// done();
});
});
});
Your browser information:
User Agent is: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
.
Challenge: Run Functional Tests using a Headless Browser
Link to the challenge: