• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 14840900305

05 May 2025 04:07PM UTC coverage: 87.864% (-1.5%) from 89.319%
14840900305

push

github

web-flow
chore: migrate tests to vitest (#3381)

managed to migrate the engine leak & memory reporters, although it took a bit of rigamarole. Mainly because, as opposed to karma, the reporter runs in node and not the browser. So I have to track the data needed separately in some global hooks _within_ the browser environment, which the reporter then reads and creates the logs if needed.

However, it seems the memory tracking is regularly reporting >1 mb, so I'm not sure if it's tracking properly or if things have changed with the new browsers. My only theory is that because the timing where memory is read is during an `afterEach` _before_ the test's `afterEach`, it's running before any potential cleanup. The timing of this is not something that's easy to control, unfortunately. I did try to prove this theory by doing the memory analysis on the next test's `beforeEach`, but it didnt seem to change the results, so it may not be an issue.

Implementation is done in `src/spec/vitest/__reporters__/memory.ts` and `src/spec/vitest/__reporters/memory.setup.ts`

---

I noticed CouroutineSpec was an offender for >10mb memory, which spins up additional engines. I added a short 100ms wait after the engine.dispose and the memory usage did drop, so it does seem like this is prone to scheduled garbage collecting.

---

I was able to run the garbage collector (if exposed, currently only on chrome) and this makes the reports more accurate

4998 of 6942 branches covered (72.0%)

13655 of 15541 relevant lines covered (87.86%)

25165.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

39.53
/src/engine/Graphics/Text.ts
1
import type { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext';
2
import type { BoundingBox } from '../Collision/BoundingBox';
3
import type { SpriteFont } from './SpriteFont';
4
import type { GraphicOptions } from './Graphic';
5
import { Graphic } from './Graphic';
6
import { Color } from '../Color';
7
import { Font } from './Font';
8

9
export interface TextOptions {
10
  /**
11
   * Text to draw
12
   */
13
  text: string;
14

15
  /**
16
   * Optionally override the font color, currently unsupported by SpriteFont
17
   */
18
  color?: Color;
19

20
  /**
21
   * Optionally specify a font, if none specified a default font is used (System sans-serif 10 pixel)
22
   */
23
  font?: Font | SpriteFont;
24

25
  /**
26
   * Optionally specify a maximum width in pixels for our text, and wrap to the next line if needed.
27
   */
28
  maxWidth?: number;
29
}
30

31
/**
32
 * Represent Text graphics in excalibur
33
 *
34
 * Useful for in game labels, ui, or overlays
35
 */
36
export class Text extends Graphic {
37
  public color?: Color;
38
  public maxWidth?: number;
39
  constructor(options: TextOptions & GraphicOptions) {
40
    super(options);
5✔
41
    // This order is important font, color, then text
42
    this.font = options.font ?? new Font();
5!
43
    this.color = options.color ?? this.color;
5!
44
    this.text = options.text;
5✔
45
    this.maxWidth = options.maxWidth;
5✔
46
  }
47

48
  public clone(): Text {
49
    return new Text({
×
50
      text: this.text.slice(),
51
      color: this.color?.clone() ?? Color.Black,
×
52
      font: this.font.clone(),
53
      maxWidth: this.maxWidth
54
    });
55
  }
56

57
  private _text: string = '';
5✔
58
  public get text() {
59
    return this._text;
2✔
60
  }
61

62
  public set text(value: string) {
63
    this._text = value;
10✔
64
    this._calculateDimension();
10✔
65
  }
66

67
  private _font!: Font | SpriteFont;
68
  public get font(): Font | SpriteFont {
69
    return this._font;
20✔
70
  }
71
  public set font(font: Font | SpriteFont) {
72
    this._font = font;
11✔
73
  }
74

75
  private _textWidth: number = 0;
5✔
76

77
  public get width() {
78
    if (this._textWidth === 0) {
×
79
      this._calculateDimension();
×
80
    }
81
    return this._textWidth * this.scale.x;
×
82
  }
83

84
  private _textHeight: number = 0;
5✔
85
  public get height() {
86
    if (this._textHeight === 0) {
×
87
      this._calculateDimension();
×
88
    }
89
    return this._textHeight * this.scale.y;
×
90
  }
91

92
  private _calculateDimension() {
93
    const { width, height } = this.font.measureText(this._text, this.maxWidth);
10✔
94
    this._textWidth = width;
10✔
95
    this._textHeight = height;
10✔
96
  }
97

98
  public get localBounds(): BoundingBox {
99
    return this.font.measureText(this._text, this.maxWidth).scale(this.scale);
10✔
100
  }
101

102
  protected override _rotate(_ex: ExcaliburGraphicsContext) {
103
    // None this is delegated to font
104
    // This override erases the default behavior
105
  }
106

107
  protected override _flip(_ex: ExcaliburGraphicsContext) {
108
    // None this is delegated to font
109
    // This override erases the default behavior
110
  }
111

112
  protected override _preDraw(ex: ExcaliburGraphicsContext, x: number, y: number): void {
113
    if (this.isStale() || this.font.isStale()) {
×
114
      this.font.flipHorizontal = this.flipHorizontal;
×
115
      this.font.flipVertical = this.flipVertical;
×
116
      this.font.rotation = this.rotation;
×
117
      this.font.origin = this.origin;
×
118
      this.font.opacity = this.opacity;
×
119
    }
120
    this.font.tint = this.tint;
×
121
    super._preDraw(ex, x, y);
×
122
  }
123

124
  protected override _drawImage(ex: ExcaliburGraphicsContext, x: number, y: number) {
125
    let color = Color.Black;
×
126
    if (this.font instanceof Font) {
×
127
      color = this.color ?? this.font.color;
×
128
    }
129

130
    const { width, height } = this.font.measureText(this._text, this.maxWidth);
×
131
    this._textWidth = width;
×
132
    this._textHeight = height;
×
133

134
    this.font.render(ex, this._text, color, x, y, this.maxWidth);
×
135

136
    if (this.font.showDebug) {
×
137
      ex.debug.drawRect(x - width, y - height, width * 2, height * 2);
×
138
      if (this.maxWidth != null) {
×
139
        ex.debug.drawRect(x, y, this.maxWidth, this.height, {
×
140
          color: Color.Yellow
141
        });
142
      }
143
    }
144
  }
145
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc