initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
.env
|
||||||
|
node_modules/
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# freeCodeCamp-Sudoku-Solver
|
||||||
|
|
||||||
|
A sudoku solver built as part of the freeCodeCamp QA certification.
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* DO NOT EDIT THIS FILE
|
||||||
|
* For FCC testing purposes!
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function objParser(str, init) {
|
||||||
|
// finds objects, arrays, strings, and function arguments
|
||||||
|
// between parens, because they may contain ','
|
||||||
|
let openSym = ['[', '{', '"', "'", '('];
|
||||||
|
let closeSym = [']', '}', '"', "'", ')'];
|
||||||
|
let type;
|
||||||
|
let i;
|
||||||
|
for (i = (init || 0); i < str.length; i++) {
|
||||||
|
type = openSym.indexOf(str[i]);
|
||||||
|
if (type !== -1) break;
|
||||||
|
}
|
||||||
|
if (type === -1) return null;
|
||||||
|
let open = openSym[type];
|
||||||
|
let close = closeSym[type];
|
||||||
|
let count = 1;
|
||||||
|
let k;
|
||||||
|
for (k = i + 1; k < str.length; k++) {
|
||||||
|
if (open === '"' || open === "'") {
|
||||||
|
if (str[k] === close) count--;
|
||||||
|
if (str[k] === '\\') k++;
|
||||||
|
} else {
|
||||||
|
if (str[k] === open) count++;
|
||||||
|
if (str[k] === close) count--;
|
||||||
|
}
|
||||||
|
if (count === 0) break;
|
||||||
|
}
|
||||||
|
if (count !== 0) return null;
|
||||||
|
let obj = str.slice(i, k + 1);
|
||||||
|
return {
|
||||||
|
start: i,
|
||||||
|
end: k,
|
||||||
|
obj: obj
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function replacer(str) {
|
||||||
|
// replace objects with a symbol ( __#n)
|
||||||
|
let obj;
|
||||||
|
let cnt = 0;
|
||||||
|
let data = [];
|
||||||
|
while (obj = objParser(str)) {
|
||||||
|
data[cnt] = obj.obj;
|
||||||
|
str = str.substring(0, obj.start) + '__#' + cnt++ + str.substring(obj.end + 1)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
str: str,
|
||||||
|
dictionary: data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitter(str) {
|
||||||
|
// split on commas, then restore the objects
|
||||||
|
let strObj = replacer(str);
|
||||||
|
let args = strObj.str.split(',');
|
||||||
|
args = args.map(function (a) {
|
||||||
|
let m = a.match(/__#(\d+)/);
|
||||||
|
while (m) {
|
||||||
|
a = a.replace(/__#(\d+)/, strObj.dictionary[m[1]]);
|
||||||
|
m = a.match(/__#(\d+)/);
|
||||||
|
}
|
||||||
|
return a.trim();
|
||||||
|
})
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertionAnalyser(body) {
|
||||||
|
|
||||||
|
// already filtered in the test runner
|
||||||
|
// // remove comments
|
||||||
|
// body = body.replace(/\/\/.*\n|\/\*.*\*\//g, '');
|
||||||
|
// // get test function body
|
||||||
|
// body = body.match(/\{\s*([\s\S]*)\}\s*$/)[1];
|
||||||
|
|
||||||
|
if (!body) return "invalid assertion";
|
||||||
|
// replace assertions bodies, so that they cannot
|
||||||
|
// contain the word 'assertion'
|
||||||
|
|
||||||
|
let cleanedBody = body.match(/(?:browser\s*\.\s*)?assert\s*\.\s*\w*\([\s\S]*\)/)
|
||||||
|
if (cleanedBody && Array.isArray(cleanedBody)) {
|
||||||
|
body = cleanedBody[0];
|
||||||
|
} else {
|
||||||
|
// No assertions present
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
let s = replacer(body);
|
||||||
|
// split on 'assertion'
|
||||||
|
let splittedAssertions = s.str.split('assert');
|
||||||
|
let assertions = splittedAssertions.slice(1);
|
||||||
|
// match the METHODS
|
||||||
|
|
||||||
|
let assertionBodies = [];
|
||||||
|
let methods = assertions.map(function (a, i) {
|
||||||
|
let m = a.match(/^\s*\.\s*(\w+)__#(\d+)/);
|
||||||
|
assertionBodies.push(parseInt(m[2]));
|
||||||
|
let pre = splittedAssertions[i].match(/browser\s*\.\s*/) ? 'browser.' : '';
|
||||||
|
return pre + m[1];
|
||||||
|
});
|
||||||
|
if (methods.some(function (m) { return !m })) return "invalid assertion";
|
||||||
|
// remove parens from the assertions bodies
|
||||||
|
let bodies = assertionBodies.map(function (b) {
|
||||||
|
return s.dictionary[b].slice(1, -1).trim();
|
||||||
|
});
|
||||||
|
assertions = methods.map(function (m, i) {
|
||||||
|
return {
|
||||||
|
method: m,
|
||||||
|
args: splitter(bodies[i]) //replace objects, split on ',' ,then restore objects
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return assertions;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = assertionAnalyser;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export const puzzlesAndSolutions = [
|
||||||
|
[
|
||||||
|
'1.5..2.84..63.12.7.2..5.....9..1....8.2.3674.3.7.2..9.47...8..1..16....926914.37.',
|
||||||
|
'135762984946381257728459613694517832812936745357824196473298561581673429269145378'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'5..91372.3...8.5.9.9.25..8.68.47.23...95..46.7.4.....5.2.......4..8916..85.72...3',
|
||||||
|
'568913724342687519197254386685479231219538467734162895926345178473891652851726943'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'..839.7.575.....964..1.......16.29846.9.312.7..754.....62..5.78.8...3.2...492...1',
|
||||||
|
'218396745753284196496157832531672984649831257827549613962415378185763429374928561'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'.7.89.....5....3.4.2..4..1.5689..472...6.....1.7.5.63873.1.2.8.6..47.1..2.9.387.6',
|
||||||
|
'473891265851726394926345817568913472342687951197254638734162589685479123219538746'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'82..4..6...16..89...98315.749.157.............53..4...96.415..81..7632..3...28.51',
|
||||||
|
'827549163531672894649831527496157382218396475753284916962415738185763249374928651'
|
||||||
|
]
|
||||||
|
];
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
class SudokuSolver {
|
||||||
|
|
||||||
|
validate(puzzleString) {
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRowPlacement(puzzleString, row, column, value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkColPlacement(puzzleString, row, column, value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRegionPlacement(puzzleString, row, column, value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
solve(puzzleString) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SudokuSolver;
|
||||||
Generated
+8345
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "sudoku-solver",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A sudoku solver built for the freeCodeCamp QA certification.",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js",
|
||||||
|
"dev": "nodemon server.js",
|
||||||
|
"test": "SET NODE_ENV=test&nodemon server.js"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.18.0",
|
||||||
|
"@babel/preset-env": "^7.18.0",
|
||||||
|
"@babel/register": "^7.17.7",
|
||||||
|
"body-parser": "^1.20.0",
|
||||||
|
"chai": "^4.3.6",
|
||||||
|
"chai-http": "^4.3.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.0.1",
|
||||||
|
"express": "^4.18.1",
|
||||||
|
"mocha": "^10.0.0",
|
||||||
|
"nodemon": "^2.0.16"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
const textArea = document.getElementById("text-input");
|
||||||
|
const coordInput = document.getElementById("coord");
|
||||||
|
const valInput = document.getElementById("val");
|
||||||
|
const errorMsg = document.getElementById("error");
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
textArea.value =
|
||||||
|
"..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..";
|
||||||
|
fillpuzzle(textArea.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
textArea.addEventListener("input", () => {
|
||||||
|
fillpuzzle(textArea.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
function fillpuzzle(data) {
|
||||||
|
let len = data.length < 81 ? data.length : 81;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
let rowLetter = String.fromCharCode('A'.charCodeAt(0) + Math.floor(i / 9));
|
||||||
|
let col = (i % 9) + 1;
|
||||||
|
if (!data[i] || data[i] === ".") {
|
||||||
|
document.getElementsByClassName(rowLetter + col)[0].innerText = " ";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
document.getElementsByClassName(rowLetter + col)[0].innerText = data[i];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSolved() {
|
||||||
|
const stuff = { "puzzle": textArea.value }
|
||||||
|
const data = await fetch("/api/solve", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(stuff)
|
||||||
|
})
|
||||||
|
const parsed = await data.json();
|
||||||
|
if (parsed.error) {
|
||||||
|
errorMsg.innerHTML = `<code>${JSON.stringify(parsed, null, 2)}</code>`;
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fillpuzzle(parsed.solution)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getChecked() {
|
||||||
|
const stuff = { "puzzle": textArea.value, "coordinate": coordInput.value, "value": valInput.value }
|
||||||
|
const data = await fetch("/api/check", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(stuff)
|
||||||
|
})
|
||||||
|
const parsed = await data.json();
|
||||||
|
errorMsg.innerHTML = `<code>${JSON.stringify(parsed, null, 2)}</code>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("solve-button").addEventListener("click", getSolved)
|
||||||
|
document.getElementById("check-button").addEventListener("click", getChecked)
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
code {
|
||||||
|
background: #d0d0d5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
max-width: 95%;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-stories {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#error-msg {
|
||||||
|
color: red;
|
||||||
|
display: inline;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sudoku-grid-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sudoku-grid {
|
||||||
|
text-align: center;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
border: 3px solid;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yAxisLegend {
|
||||||
|
margin-top: 50px;
|
||||||
|
text-align: right;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yAxisLegend tr td {
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0);
|
||||||
|
width: 2em;
|
||||||
|
height: 1.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xAxisLegend {
|
||||||
|
border: 3px solid rgba(0, 0, 0, 0);
|
||||||
|
border-spacing: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xAxisLegend tr td {
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0);
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: bottom;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xAxisLegend tr td:nth-of-type(3n) {
|
||||||
|
border-right: 3px solid rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sudoku-input {
|
||||||
|
border: 1px solid black;
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.C1,
|
||||||
|
.C2,
|
||||||
|
.C3,
|
||||||
|
.C4,
|
||||||
|
.C5,
|
||||||
|
.C6,
|
||||||
|
.C7,
|
||||||
|
.C8,
|
||||||
|
.C9,
|
||||||
|
.F1,
|
||||||
|
.F2,
|
||||||
|
.F3,
|
||||||
|
.F4,
|
||||||
|
.F5,
|
||||||
|
.F6,
|
||||||
|
.F7,
|
||||||
|
.F8,
|
||||||
|
.F9 {
|
||||||
|
border-bottom: 3px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.A3,
|
||||||
|
.B3,
|
||||||
|
.C3,
|
||||||
|
.D3,
|
||||||
|
.E3,
|
||||||
|
.F3,
|
||||||
|
.G3,
|
||||||
|
.H3,
|
||||||
|
.I3,
|
||||||
|
.A6,
|
||||||
|
.B6,
|
||||||
|
.C6,
|
||||||
|
.D6,
|
||||||
|
.E6,
|
||||||
|
.F6,
|
||||||
|
.G6,
|
||||||
|
.H6,
|
||||||
|
.I6 {
|
||||||
|
border-right: 3px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.container {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const SudokuSolver = require('../controllers/sudoku-solver.js');
|
||||||
|
|
||||||
|
module.exports = function (app) {
|
||||||
|
|
||||||
|
let solver = new SudokuSolver();
|
||||||
|
|
||||||
|
app.route('/api/check')
|
||||||
|
.post((req, res) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.route('/api/solve')
|
||||||
|
.post((req, res) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* DO NOT EDIT THIS FILE
|
||||||
|
* For FCC testing purposes!
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const cors = require('cors');
|
||||||
|
const fs = require('fs');
|
||||||
|
const runner = require('../test-runner');
|
||||||
|
|
||||||
|
module.exports = function (app) {
|
||||||
|
|
||||||
|
app.route('/_api/server.js')
|
||||||
|
.get(function (req, res, next) {
|
||||||
|
console.log('requested');
|
||||||
|
fs.readFile(__dirname + '/server.js', function (err, data) {
|
||||||
|
if (err) return next(err);
|
||||||
|
res.send(data.toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.route('/_api/routes/api.js')
|
||||||
|
.get(function (req, res, next) {
|
||||||
|
console.log('requested');
|
||||||
|
fs.readFile(__dirname + '/routes/api.js', function (err, data) {
|
||||||
|
if (err) return next(err);
|
||||||
|
res.type('txt').send(data.toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.route('/_api/controllers/convertHandler.js')
|
||||||
|
.get(function (req, res, next) {
|
||||||
|
console.log('requested');
|
||||||
|
fs.readFile(__dirname + '/controllers/convertHandler.js', function (err, data) {
|
||||||
|
if (err) return next(err);
|
||||||
|
res.type('txt').send(data.toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/_api/get-tests', cors(), function (req, res, next) {
|
||||||
|
console.log('requested');
|
||||||
|
if (process.env.NODE_ENV === 'test') return next();
|
||||||
|
res.json({ status: 'unavailable' });
|
||||||
|
},
|
||||||
|
function (req, res, next) {
|
||||||
|
if (!runner.report) return next();
|
||||||
|
res.json(testFilter(runner.report, req.query.type, req.query.n));
|
||||||
|
},
|
||||||
|
function (req, res) {
|
||||||
|
runner.on('done', function (report) {
|
||||||
|
process.nextTick(() => res.json(testFilter(runner.report, req.query.type, req.query.n)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.get('/_api/app-info', function (req, res) {
|
||||||
|
let hs = Object.keys(res._headers)
|
||||||
|
.filter(h => !h.match(/^access-control-\w+/));
|
||||||
|
let hObj = {};
|
||||||
|
hs.forEach(h => { hObj[h] = res._headers[h] });
|
||||||
|
delete res._headers['strict-transport-security'];
|
||||||
|
res.json({ headers: hObj });
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function testFilter(tests, type, n) {
|
||||||
|
let out;
|
||||||
|
switch (type) {
|
||||||
|
case 'unit':
|
||||||
|
out = tests.filter(t => t.context.match('Unit Tests'));
|
||||||
|
break;
|
||||||
|
case 'functional':
|
||||||
|
out = tests.filter(t => t.context.match('Functional Tests') && !t.title.match('#example'));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out = tests;
|
||||||
|
}
|
||||||
|
if (n !== undefined) {
|
||||||
|
return out[n] || out;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const express = require('express');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const expect = require('chai').expect;
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
const fccTestingRoutes = require('./routes/fcctesting.js');
|
||||||
|
const apiRoutes = require('./routes/api.js');
|
||||||
|
const runner = require('./test-runner');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use('/public', express.static(process.cwd() + '/public'));
|
||||||
|
app.use(cors({ origin: '*' })); //For FCC testing purposes only
|
||||||
|
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
//Index page (static HTML)
|
||||||
|
app.route('/')
|
||||||
|
.get(function (req, res) {
|
||||||
|
res.sendFile(process.cwd() + '/views/index.html');
|
||||||
|
});
|
||||||
|
|
||||||
|
//For FCC testing purposes
|
||||||
|
fccTestingRoutes(app);
|
||||||
|
|
||||||
|
// User routes
|
||||||
|
apiRoutes(app);
|
||||||
|
|
||||||
|
//404 Not Found Middleware
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
res.status(404)
|
||||||
|
.type('text')
|
||||||
|
.send('Not Found');
|
||||||
|
});
|
||||||
|
|
||||||
|
//Start our server and tests!
|
||||||
|
const PORT = process.env.PORT || 3000
|
||||||
|
app.listen(PORT, function () {
|
||||||
|
console.log("Listening on port " + PORT);
|
||||||
|
// process.env.NODE_ENV='test'
|
||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
console.log('Running Tests...');
|
||||||
|
setTimeout(function () {
|
||||||
|
try {
|
||||||
|
runner.run();
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Tests are not valid:');
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}, 3500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app; // for testing
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* DO NOT EDIT THIS FILE
|
||||||
|
* For FCC testing purposes!
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const analyser = require('./assertion-analyser');
|
||||||
|
const EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
const Mocha = require('mocha'),
|
||||||
|
fs = require('fs'),
|
||||||
|
path = require('path');
|
||||||
|
require("@babel/register");
|
||||||
|
|
||||||
|
const mocha = new Mocha({ timeout: 5000 });
|
||||||
|
const testDir = './tests'
|
||||||
|
|
||||||
|
|
||||||
|
// Add each .js file to the mocha instance
|
||||||
|
fs.readdirSync(testDir).filter(function (file) {
|
||||||
|
// Only keep the .js files
|
||||||
|
return file.substr(-3) === '.js';
|
||||||
|
|
||||||
|
}).forEach(function (file) {
|
||||||
|
mocha.addFile(
|
||||||
|
path.join(testDir, file)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let emitter = new EventEmitter();
|
||||||
|
emitter.run = function () {
|
||||||
|
|
||||||
|
let tests = [];
|
||||||
|
let context = "";
|
||||||
|
let separator = ' -> ';
|
||||||
|
// Run the tests.
|
||||||
|
try {
|
||||||
|
let runner = mocha.ui('tdd').run()
|
||||||
|
.on('test end', function (test) {
|
||||||
|
// remove comments
|
||||||
|
let body = test.body.replace(/\/\/.*\n|\/\*.*\*\//g, '');
|
||||||
|
// collapse spaces
|
||||||
|
body = body.replace(/\s+/g, ' ');
|
||||||
|
let obj = {
|
||||||
|
title: test.title,
|
||||||
|
context: context.slice(0, -separator.length),
|
||||||
|
state: test.state,
|
||||||
|
// body: body,
|
||||||
|
assertions: analyser(body)
|
||||||
|
};
|
||||||
|
tests.push(obj);
|
||||||
|
})
|
||||||
|
.on('end', function () {
|
||||||
|
emitter.report = tests;
|
||||||
|
emitter.emit('done', tests)
|
||||||
|
})
|
||||||
|
.on('suite', function (s) {
|
||||||
|
context += (s.title + separator);
|
||||||
|
|
||||||
|
})
|
||||||
|
.on('suite end', function (s) {
|
||||||
|
context = context.slice(0, -(s.title.length + separator.length))
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
throw (e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = emitter;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mocha.runner Events:
|
||||||
|
* can be used to build a better custom report
|
||||||
|
*
|
||||||
|
* - `start` execution started
|
||||||
|
* - `end` execution complete
|
||||||
|
* - `suite` (suite) test suite execution started
|
||||||
|
* - `suite end` (suite) all tests (and sub-suites) have finished
|
||||||
|
* - `test` (test) test execution started
|
||||||
|
* - `test end` (test) test completed
|
||||||
|
* - `hook` (hook) hook execution started
|
||||||
|
* - `hook end` (hook) hook complete
|
||||||
|
* - `pass` (test) test passed
|
||||||
|
* - `fail` (test, err) test failed
|
||||||
|
* - `pending` (test) test pending
|
||||||
|
*/
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const chai = require('chai');
|
||||||
|
const assert = chai.assert;
|
||||||
|
|
||||||
|
const Solver = require('../controllers/sudoku-solver.js');
|
||||||
|
let solver;
|
||||||
|
|
||||||
|
suite('UnitTests', () => {
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const chai = require("chai");
|
||||||
|
const chaiHttp = require('chai-http');
|
||||||
|
const assert = chai.assert;
|
||||||
|
const server = require('../server');
|
||||||
|
|
||||||
|
chai.use(chaiHttp);
|
||||||
|
|
||||||
|
suite('Functional Tests', () => {
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,352 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Sudoku Solver</title>
|
||||||
|
<meta name="description" content="An example for the fCC QA Sudoku Solver project" />
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link id="favicon" rel="icon" href="https://cdn.freecodecamp.org/universal/favicons/favicon-32x32.png"
|
||||||
|
type="image/x-icon">
|
||||||
|
<link rel="stylesheet" href="../public/style.css" type="text/css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Sudoku Solver</h1>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-container">
|
||||||
|
<form id="solve-form">
|
||||||
|
<textarea rows="10" cols="85" id="text-input" name="puzzle"></textarea>
|
||||||
|
<br />
|
||||||
|
<input type="button" value="Solve" id="solve-button" />
|
||||||
|
<div id="error-msg"></div>
|
||||||
|
</form>
|
||||||
|
<form id="check-form">
|
||||||
|
<p>Coordinate (A1): <input id="coord" class="checker" type="text" name="coordinate"></p>
|
||||||
|
<p>Value (1-9): <input class="checker" type="text" id="val" name="value"></p>
|
||||||
|
<input type="button" id="check-button" value="Check Placement">
|
||||||
|
</form>
|
||||||
|
<span id="error"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="sudoku-grid-container">
|
||||||
|
<div>
|
||||||
|
<table class="yAxisLegend">
|
||||||
|
<tr>
|
||||||
|
<td>A</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>B</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>C</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>D</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>E</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>F</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>G</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>H</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>I</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="sudoku-grid">
|
||||||
|
<table class="xAxisLegend">
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>2</td>
|
||||||
|
<td>3</td>
|
||||||
|
<td>4</td>
|
||||||
|
<td>5</td>
|
||||||
|
<td>6</td>
|
||||||
|
<td>7</td>
|
||||||
|
<td>8</td>
|
||||||
|
<td>9</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table class="grid">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="A1 sudoku-input" title="A1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A2 sudoku-input" title="A2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A3 sudoku-input" title="A3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A4 sudoku-input" title="A4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A5 sudoku-input" title="A5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A6 sudoku-input" title="A6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A7 sudoku-input" title="A7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A8 sudoku-input" title="A8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="A9 sudoku-input" title="A9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="B1 sudoku-input" title="B1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B2 sudoku-input" title="B2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B3 sudoku-input" title="B3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B4 sudoku-input" title="B4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B5 sudoku-input" title="B5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B6 sudoku-input" title="B6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B7 sudoku-input" title="B7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B8 sudoku-input" title="B8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="B9 sudoku-input" title="B9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="C1 sudoku-input" title="C1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C2 sudoku-input" title="C2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C3 sudoku-input" title="C3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C4 sudoku-input" title="C4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C5 sudoku-input" title="C5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C6 sudoku-input" title="C6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C7 sudoku-input" title="C7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C8 sudoku-input" title="C8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="C9 sudoku-input" title="C9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="D1 sudoku-input" title="D1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D2 sudoku-input" title="D2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D3 sudoku-input" title="D3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D4 sudoku-input" title="D4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D5 sudoku-input" title="D5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D6 sudoku-input" title="D6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D7 sudoku-input" title="D7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D8 sudoku-input" title="D8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="D9 sudoku-input" title="D9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="E1 sudoku-input" title="E1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E2 sudoku-input" title="E2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E3 sudoku-input" title="E3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E4 sudoku-input" title="E4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E5 sudoku-input" title="E5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E6 sudoku-input" title="E6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E7 sudoku-input" title="E7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E8 sudoku-input" title="E8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="E9 sudoku-input" title="E9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="F1 sudoku-input" title="F1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F2 sudoku-input" title="F2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F3 sudoku-input" title="F3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F4 sudoku-input" title="F4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F5 sudoku-input" title="F5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F6 sudoku-input" title="F6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F7 sudoku-input" title="F7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F8 sudoku-input" title="F8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="F9 sudoku-input" title="F9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="G1 sudoku-input" title="G1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G2 sudoku-input" title="G2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G3 sudoku-input" title="G3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G4 sudoku-input" title="G4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G5 sudoku-input" title="G5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G6 sudoku-input" title="G6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G7 sudoku-input" title="G7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G8 sudoku-input" title="G8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="G9 sudoku-input" title="G9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="H1 sudoku-input" title="H1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H2 sudoku-input" title="H2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H3 sudoku-input" title="H3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H4 sudoku-input" title="H4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H5 sudoku-input" title="H5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H6 sudoku-input" title="H6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H7 sudoku-input" title="H7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H8 sudoku-input" title="H8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="H9 sudoku-input" title="H9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="I1 sudoku-input" title="I1">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I2 sudoku-input" title="I2">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I3 sudoku-input" title="I3">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I4 sudoku-input" title="I4">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I5 sudoku-input" title="I5">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I6 sudoku-input" title="I6">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I7 sudoku-input" title="I7">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I8 sudoku-input" title="I8">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="I9 sudoku-input" title="I9">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script type="module" src="../public/index.js"></script>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user