实例化对象导致测试中断

时间:2018-04-10 18:59:38

标签: typescript jasmine karma-jasmine

我无法解决阻止我测试的错误。错误是:

  {
    "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);
}

0 个答案:

没有答案