block.test.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { describe, it, expect } from 'vitest';
  2. import * as fc from 'fast-check';
  3. import { Block } from '../src/entities/Block.js';
  4. import { GRID_GAP } from '../src/constants.js';
  5. describe('Block', () => {
  6. describe('constructor', () => {
  7. it('initializes with correct properties', () => {
  8. const block = new Block(3, 2, 5, 40);
  9. expect(block.gridX).toBe(3);
  10. expect(block.gridY).toBe(2);
  11. expect(block.count).toBe(5);
  12. expect(block.size).toBe(40);
  13. expect(block.destroyed).toBe(false);
  14. });
  15. });
  16. describe('hit()', () => {
  17. it('decrements count by 1', () => {
  18. const block = new Block(0, 0, 5, 40);
  19. block.hit();
  20. expect(block.count).toBe(4);
  21. });
  22. it('returns false when count > 0 after hit', () => {
  23. const block = new Block(0, 0, 3, 40);
  24. expect(block.hit()).toBe(false);
  25. });
  26. it('marks destroyed and returns true when count reaches 0', () => {
  27. const block = new Block(0, 0, 1, 40);
  28. expect(block.hit()).toBe(true);
  29. expect(block.destroyed).toBe(true);
  30. expect(block.count).toBe(0);
  31. });
  32. it('marks destroyed when count goes below 0', () => {
  33. const block = new Block(0, 0, 0, 40);
  34. expect(block.hit()).toBe(true);
  35. expect(block.destroyed).toBe(true);
  36. expect(block.count).toBe(-1);
  37. });
  38. });
  39. describe('getRect()', () => {
  40. it('converts grid coordinates to pixel coordinates', () => {
  41. const size = 40;
  42. const block = new Block(2, 3, 5, size);
  43. const rect = block.getRect();
  44. expect(rect.x).toBe(2 * (size + GRID_GAP) + GRID_GAP);
  45. expect(rect.y).toBe(3 * (size + GRID_GAP) + GRID_GAP);
  46. expect(rect.width).toBe(size);
  47. expect(rect.height).toBe(size);
  48. });
  49. it('returns correct rect for origin block (0,0)', () => {
  50. const size = 50;
  51. const block = new Block(0, 0, 1, size);
  52. const rect = block.getRect();
  53. expect(rect.x).toBe(GRID_GAP);
  54. expect(rect.y).toBe(GRID_GAP);
  55. expect(rect.width).toBe(size);
  56. expect(rect.height).toBe(size);
  57. });
  58. });
  59. describe('getColor()', () => {
  60. it('returns a valid hex color string', () => {
  61. const block = new Block(0, 0, 10, 40);
  62. const color = block.getColor();
  63. expect(color).toMatch(/^#[0-9a-f]{6}$/);
  64. });
  65. it('returns different colors for different counts', () => {
  66. const block1 = new Block(0, 0, 1, 40);
  67. const block2 = new Block(0, 0, 50, 40);
  68. expect(block1.getColor()).not.toBe(block2.getColor());
  69. });
  70. });
  71. describe('isDestroyed()', () => {
  72. it('returns false when count > 0', () => {
  73. const block = new Block(0, 0, 5, 40);
  74. expect(block.isDestroyed()).toBe(false);
  75. });
  76. it('returns true when count is 0', () => {
  77. const block = new Block(0, 0, 0, 40);
  78. expect(block.isDestroyed()).toBe(true);
  79. });
  80. it('returns true when count is negative', () => {
  81. const block = new Block(0, 0, -1, 40);
  82. expect(block.isDestroyed()).toBe(true);
  83. });
  84. });
  85. describe('moveDown()', () => {
  86. it('increments gridY by 1', () => {
  87. const block = new Block(3, 2, 5, 40);
  88. block.moveDown();
  89. expect(block.gridY).toBe(3);
  90. });
  91. it('can be called multiple times', () => {
  92. const block = new Block(0, 0, 5, 40);
  93. block.moveDown();
  94. block.moveDown();
  95. block.moveDown();
  96. expect(block.gridY).toBe(3);
  97. });
  98. });
  99. // Feature: ball-block-breaker, Property 7: 碰撞方块数字减1
  100. // **Validates: Requirements 4.1**
  101. describe('Property 7: 碰撞方块数字减1', () => {
  102. it('hit() decrements count by exactly 1 for any block with count > 0', () => {
  103. fc.assert(
  104. fc.property(
  105. fc.integer({ min: 1, max: 10000 }),
  106. fc.integer({ min: 0, max: 6 }),
  107. fc.integer({ min: 0, max: 20 }),
  108. fc.constantFrom(30, 40, 50),
  109. (count, gridX, gridY, size) => {
  110. const block = new Block(gridX, gridY, count, size);
  111. const originalCount = block.count;
  112. block.hit();
  113. expect(block.count).toBe(originalCount - 1);
  114. }
  115. ),
  116. { numRuns: 100 }
  117. );
  118. });
  119. });
  120. });