我无法解决阻止我测试的错误。错误是:
{
"message": "TypeError: shipContainer is null\nat spec/gameboard_spec.ts:138:13\n\nShip.prototype.renderShip/<@spec/gameboard_spec.ts:138:13\nShip.prototype.renderShip@spec/gameboard_spec.ts:129:9\n@spec/gameboard_spec.ts:169:5\n@spec/gameboard_spec.ts:163:2\n__webpack_require__@spec/gameboard_spec.ts:20:12\n@spec/gameboard_spec.ts:228:64\n__webpack_require__@spec/gameboard_spec.ts:20:12\n@spec/gameboard_spec.ts:187:72\n__webpack_require__@spec/gameboard_spec.ts:20:12\n@spec/gameboard_spec.ts:69:18\n@spec/gameboard_spec.ts:1:11\n",
"str": "TypeError: shipContainer is null\nat spec/gameboard_spec.ts:138:13\n\nShip.prototype.renderShip/<@spec/gameboard_spec.ts:138:13\nShip.prototype.renderShip@spec/gameboard_spec.ts:129:9\n@spec/gameboard_spec.ts:169:5\n@spec/gameboard_spec.ts:163:2\n__webpack_require__@spec/gameboard_spec.ts:20:12\n@spec/gameboard_spec.ts:228:64\n__webpack_require__@spec/gameboard_spec.ts:20:12\n@spec/gameboard_spec.ts:187:72\n__webpack_require__@spec/gameboard_spec.ts:20:12\n@spec/gameboard_spec.ts:69:18\n@spec/gameboard_spec.ts:1:11\n"
}
现在我知道它说我的shipContainer变量为null,我把它归结为特定的代码行:
renderShip(): HTMLElement {
// const j = jQuery.noConflict();
const ship: number[] = this.createShip();
const shipContainer: HTMLElement = document.getElementById(this.type());
console.log(this.type());
console.log(document);
// console.log(document.getElementById(this.type()));
ship.forEach((value: number, index: number) => {
const section: HTMLElement = document.createElement('div');
section.setAttribute('draggable', 'true');
section.setAttribute('aria-grabbed', 'false');
section.setAttribute('data-direction', 'horizontal');
section.setAttribute('data-ship-type', this.type());
section.classList.add('ship');
section.classList.add(this.type());
section.id = this.type() + String(index);
shipContainer.appendChild(section);
});
shipContainer.addEventListener('click', () => {
const ship = shipContainer.children;
for (let i = 0; i < ship.length; i = i + 1) {
ship[i].setAttribute('aria-grabbed', 'true');
}
});
shipContainer.setAttribute('data-direction', 'horizontal');
shipContainer.addEventListener('dragstart', (e: any) => {
drag(e);
});
return shipContainer;
}
主要是这一行:
const shipContainer: HTMLElement = document.getElementById(this.type());
据说没有带有生成ID的dom元素。但是,有一些代码在浏览器中运行良好。这是我的HTML副本(index.html和SpecRunner.html):
<!DOCTYPE html>
<html>
<head>
<title>Battleship</title>
<link rel="stylesheet" type="text/css" href="css/styles.css">
</head>
<body>
<div class="feedback"></div>
<section class="game">
<div class="container"></div>
<section class="ships">
<div class="ship_container" id="carrier"></div>
<div class="ship_container" id="battleship"></div>
<div class="ship_container" id="submarine"></div>
<div class="ship_container" id="destroyer"></div>
<div class="ship_container" id="patrol"></div>
</section>
</section>
<script type="text/javascript" src="dist/bundle.js"></script>
</body>
</html>
那里有船只类型的所有ID。当我去实例化一个测试对象时,所有的失败都会发生。测试看起来像这样:
import { Gameboard } from '../src/gameboard';
describe('Gameboard', () => {
const gameboard = new Gameboard();
// const grid: any[] = gameboard.createGrid();
// let layout;
// let container: HTMLElement;
beforeEach(() => {
// layout = gameboard.renderGrid();
// container = document.querySelector('.container');
// spyOn(gameboard, 'createGrid');
});
describe('createGrid', () => {
it('should have a grid of 100 cells', () => {
// expect(gameboard.createGrid).toHaveBeenCalled();
});
// it('should have a grid of 100 cells', () => {
// expect(grid.length).toBe(100);
// });
});
// describe('renderGrid', () => {
// it('should have a representation of 100 cells', () => {
// expect(container).toExist();
// expect(container.children.length).toBe(100);
// });
// it('should have cells of class square', () => {
// const cells = container.children.length;
// for (let i = 0; i < cells; i = i + 1) {
// expect(container.children[i]).toHaveClass('square');
// }
// });
// });
});
因此,const gameboard = new Gameboard();
会导致shipContainer变为空。
我不确定它是否与业力和茉莉有关。或者,也许是webpack。我说不出来。我写的测试错了什么的。我真的需要帮助。非常欢迎任何见解。
修改
Gameboard.ts
import { Ship } from './ship';
import { Ships as ships } from './ships';
export class Gameboard {
createGrid(): any[] {
const grid: any[] = [];
let row: number;
let column: number;
for (row = 0; row < 10; row = row + 1) {
for (column = 0; column < 10; column = column + 1) {
grid.push([column, row]);
}
}
return grid;
}
renderGrid(): HTMLElement {
const grid: any[] = this.createGrid();
const container: HTMLElement = document.querySelector('.container');
grid.forEach((value: string, index: number) => {
const cell: HTMLElement = document.createElement('div');
cell.addEventListener('drop', (e: any) => {
drop(e);
});
cell.addEventListener('dragover', (e: any) => {
allowDrop(e);
});
cell.addEventListener('dragstart', (e: any) => {
drag(e);
});
cell.addEventListener('dblclick', (e: any) => {
change_direction(e);
});
cell.addEventListener('click', (e: any) => {
this.recieveAttack(e);
});
cell.setAttribute('draggable', 'false');
cell.classList.add('square');
cell.id = grid[index];
// cell.innerHTML = grid[index];
container.appendChild(cell);
});
container.addEventListener('mouseup', unFocus);
container.addEventListener('mousemove', unFocus);
return container;
}
miss(target: string): void {
const location = document.getElementById(target);
location.classList.add('miss');
}
recieveAttack(e: any): void {
const location = document.getElementById(e.target.id);
type ships = { [key: string]: Ship };
const ships2: ships = { ...ships };
let target: string;
if (location.hasChildNodes()) {
target = e.currentTarget.children[0].getAttribute('data-ship-type');
}
if (location.classList.contains('occupied')) {
console.log(target);
console.log('hit', ships2[target]);
try {
ships2[target].hit(e.target.id);
} catch (error) {
alert('runtime error ' + error.message);
}
} else {
this.miss(e.target.id);
}
}
}
function allowDrop(e: Event): void {
e.preventDefault();
}
function drop(e: any): void {
e.preventDefault();
const data = e.dataTransfer.getData('text/html');
const horizontal = document.getElementById(data);
const coordinate = e.target.id;
const sections = document.getElementsByClassName(horizontal.className);
const x = Number(coordinate[0]);
const y = Number(coordinate[coordinate.length - 1]);
let len;
let id;
let outOfBounds;
if (horizontal.hasChildNodes()) {
len = horizontal.children.length;
id = horizontal.children[0].id.slice(0, -1);
outOfBounds = horizontal.children;
} else {
len = sections.length;
id = horizontal.id.slice(0, -1);
outOfBounds = sections;
}
if (horizontal.getAttribute('data-direction') === 'horizontal') {
if (!preventOutOfBounds(outOfBounds, x, y) && !illegalPlacement(outOfBounds, x, y)) {
for (let i = 0; i < len ; i = i + 1) {
const rangeX = x + i;
document.getElementById(`${ rangeX },${ y }`)
.appendChild(document.getElementById(String(id + i)));
}
}
} else {
if (!preventOutOfBounds(outOfBounds, x, y) && !illegalPlacement(outOfBounds, x, y)) {
for (let i = 0; i < len; i = i + 1) {
const rangeY = y - i;
document.getElementById(`${ x },${ rangeY }`)
.appendChild(document.getElementById(String(id + i)));
}
}
}
e.dataTransfer.clearData();
}
function drag(e: any): void {
e.dataTransfer.setData('text/html', e.currentTarget.children[0].id);
}
function change_direction(e: any): void {
const targets = document.getElementsByClassName(e.currentTarget.children[0].className);
const coordinate = e.target.id;
const x = Number(coordinate[0]);
const y = Number(coordinate[coordinate.length - 1]);
if (targets[0].getAttribute('data-direction') === 'horizontal') {
if (!preventOutOfBounds(targets, x, y) && !check_collision(targets, x, y)) {
for (let i = 0; i < targets.length; i = i + 1) {
const rangeY = y - i;
document.getElementById(`${ x },${ rangeY }`)
.appendChild(document.querySelector(`#${ String(targets[i].id.slice(0, -1) + i) }.ship`));
document.querySelector(String(`#${ targets[i].id.slice(0, -1) + i }.ship`))
.setAttribute('data-direction', 'vertical');
}
}
} else {
if (!preventOutOfBounds(targets, x, y) && !check_collision(targets, x, y)) {
for (let i = 0; i < targets.length; i = i + 1) {
const rangeX = x + i;
document.getElementById(`${ rangeX },${ y }`)
.appendChild(document.querySelector(`#${ String(targets[i].id.slice(0, -1) + i) }.ship`));
document.querySelector(String(`#${ targets[i].id.slice(0, -1) + i }.ship`))
.setAttribute('data-direction', 'horizontal');
}
}
}
}
function check_collision(ship: any, x: number, y: number): boolean {
for (let i = 0; i < ship.length - 1; i = i + 1) {
if (ship[0].getAttribute('data-direction') === 'horizontal') {
const location = document.getElementById(`${ String(x) },${ String(y - i - 1) }`);
const feedback = document.querySelector('.feedback');
if (location.classList.contains('occupied')) {
feedback.innerHTML = 'Illegal Move';
return true;
}
} else {
const location = document.getElementById(`${ String(x + i + 1) },${ String(y) }`);
const feedback = document.querySelector('.feedback');
if (location.classList.contains('occupied')) {
feedback.innerHTML = 'Illegal Move';
return true;
}
}
}
}
function preventOutOfBounds(ship: any, x: number, y: number): boolean {
for (let i = 0; i < ship.length; i = i + 1) {
if (ship[0].getAttribute('data-direction') === 'horizontal') {
const location = document.getElementById(`${ String(x + i) },${ String(y) }`);
const feedback = document.querySelector('.feedback');
if (Number(location.id[0]) > 9) {
return true;
}
} else {
const location = document.getElementById(`${ String(x) },${ String(y - i) }`);
const feedback = document.querySelector('.feedback');
if (Number(location.id[location.id.length - 1]) < 0) {
return true;
}
}
}
}
function illegalPlacement(ship: any, x: number, y: number): boolean {
for (let i = 0; i < ship.length; i = i + 1) {
if (ship[0].getAttribute('data-direction') === 'horizontal') {
const location = document.getElementById(`${ String(x + i) },${ String(y) }`);
const feedback = document.querySelector('.feedback');
if (location.classList.contains('occupied')) {
feedback.innerHTML = 'Illegal placement';
return true;
}
} else {
const location = document.getElementById(`${ String(x) },${ String(y - i) }`);
const feedback = document.querySelector('.feedback');
if (location.classList.contains('occupied')) {
feedback.innerHTML = 'Illegal placement';
return true;
}
}
}
}
function unFocus(): void {
if (document.getSelection()) {
document.getSelection().empty();
} else {
window.getSelection().removeAllRanges();
}
}
Ship.ts
export class Ship {
length: number;
health: number;
sunk: boolean;
constructor(length: number) {
this.length = length;
this.health = length;
this.sunk = false;
}
type(): string {
if (this.length === 2) {
return 'patrol';
}
if (this.length === 3 && document.body.contains(document.getElementById('submarine0'))
&& document.body.contains(document.getElementById('submarine1'))
&& document.body.contains(document.getElementById('submarine2'))
) {
return 'destroyer';
}
if (this.length === 3) {
return 'submarine';
}
if (this.length === 4) {
return 'battleship';
}
if (this.length === 5) {
return 'carrier';
}
}
hit(target: string): void {
const location = document.getElementById(target);
location.classList.add('hit');
this.health = this.health - 1;
}
isSunk(): void {
if (this.health === 0) {
this.sunk = true;
}
}
createShip(): number[] {
const ship: number[] = [];
for (let size = 0; size < this.length; size = size + 1) {
ship.push(size);
}
return ship;
}
renderShip(): HTMLElement {
// const j = jQuery.noConflict();
const ship: number[] = this.createShip();
const shipContainer: HTMLElement = document.getElementById(this.type());
console.log(this.type());
console.log(document);
// console.log(document.getElementById(this.type()));
ship.forEach((value: number, index: number) => {
const section: HTMLElement = document.createElement('div');
section.setAttribute('draggable', 'true');
section.setAttribute('aria-grabbed', 'false');
section.setAttribute('data-direction', 'horizontal');
section.setAttribute('data-ship-type', this.type());
section.classList.add('ship');
section.classList.add(this.type());
section.id = this.type() + String(index);
shipContainer.appendChild(section);
});
shipContainer.addEventListener('click', () => {
const ship = shipContainer.children;
for (let i = 0; i < ship.length; i = i + 1) {
ship[i].setAttribute('aria-grabbed', 'true');
}
});
shipContainer.setAttribute('data-direction', 'horizontal');
shipContainer.addEventListener('dragstart', (e: any) => {
drag(e);
});
return shipContainer;
}
}
function drag(e: any): void {
e.dataTransfer.setData('text/html', e.target.parentNode.id);
}