chromium/chrome/test/data/webui/chromeos/diagnostics/drawing_provider_test.ts

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'chrome://diagnostics/strings.m.js';
import 'chrome://webui-test/chromeos/mojo_webui_test_support.js';

import {CanvasDrawingProvider} from 'chrome://diagnostics/drawing_provider.js';
import {constructRgba, DESTINATION_OVER, LINE_CAP, LINE_WIDTH, lookupCssVariableValue, MARK_COLOR, MARK_OPACITY, MARK_RADIUS, TRAIL_COLOR, TRAIL_MAX_OPACITY} from 'chrome://diagnostics/drawing_provider_utils.js';
import {assertNotReached} from 'chrome://resources/js/assert.js';
import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chromeos/chai_assert.js';
import {MockController} from 'chrome://webui-test/mock_controller.js';

/**
 * FakeCanvasCtx class mocks various html Canvas API methods to make it easy to
 * test the drawing behavior.
 */
class FakeCanvasCtx implements Partial<CanvasRenderingContext2D> {
  mock: string[] = [];

  getMock(): string[] {
    return this.mock;
  }

  beginPath(): void {
    this.mock.push('beginPath');
  }

  stroke(): void {
    this.mock.push('stroke');
  }

  fill(): void {
    this.mock.push('fill');
  }

  moveTo(x: number, y: number): void {
    this.mock.push(`moveTo:${x}~${y}`);
  }

  lineTo(x: number, y: number): void {
    this.mock.push(`lineTo:${x}~${y}`);
  }

  arc(x: number, y: number, r: number, startAngle: number,
      endAngle: number): void {
    this.mock.push(`arc:${x}~${y}~${r}~${startAngle}~${endAngle}`);
  }
}

suite('drawingProviderTestSuite', function() {
  const mockController = new MockController();

  setup(() => {
    // Setup mock for window.getComputedStyle function to prevent test flaky.
    const mockComputedStyle =
        mockController.createFunctionMock(window, 'getComputedStyle');
    mockComputedStyle.returnValue = {
      getPropertyValue: (valName: string) => {
        switch (valName) {
          case TRAIL_COLOR:
            return 'rgb(220, 210, 155)';
          case MARK_COLOR:
            return 'rgb(198, 179, 165)';
          case MARK_OPACITY:
            return '0.7';
          default:
            assertNotReached();
        }
      },
    };
  });

  teardown(() => {
    mockController.reset();
  });

  function initializeDrawingProvider(): CanvasDrawingProvider {
    return new CanvasDrawingProvider(
        new FakeCanvasCtx() as unknown as CanvasRenderingContext2D);
  }

  test('SettingUpCanvasDrawingProvider', () => {
    const drawingProvider = initializeDrawingProvider();
    assertEquals(LINE_CAP, drawingProvider.getLineCap());
    assertEquals(LINE_WIDTH, drawingProvider.getLineWidth());
  });

  test('TestDrawTrail', () => {
    const x0 = 10;
    const y0 = 15;
    const x1 = 20;
    const y1 = 25;
    const pressure = 100;
    const expectedMock = [
      'beginPath',
      `moveTo:${x0}~${y0}`,
      `lineTo:${x1}~${y1}`,
      'stroke',
    ];

    const drawingProvider = initializeDrawingProvider();
    drawingProvider.drawTrail(x0, y0, x1, y1, pressure);

    const expectedStrokeStyle = constructRgba(
        lookupCssVariableValue(TRAIL_COLOR), `${TRAIL_MAX_OPACITY * pressure}`);

    assertDeepEquals(
        expectedMock,
        (drawingProvider.getCtx() as unknown as FakeCanvasCtx).getMock());
    assertEquals(expectedStrokeStyle, drawingProvider.getStrokeStyle());
  });

  test('TestDrawTrailMark', () => {
    const x = 10;
    const y = 15;
    const expectedMock = [
      'beginPath',
      `arc:${x}~${y}~${MARK_RADIUS}~${0}~${2 * Math.PI}`,
      'fill',
    ];

    const drawingProvider = initializeDrawingProvider();
    drawingProvider.drawTrailMark(x, y);

    const expectedFillStyle = constructRgba(
        lookupCssVariableValue(MARK_COLOR),
        lookupCssVariableValue(MARK_OPACITY));

    assertDeepEquals(
        expectedMock,
        (drawingProvider.getCtx() as unknown as FakeCanvasCtx).getMock());
    assertEquals(expectedFillStyle, drawingProvider.getFillStyle());
    assertEquals(
        DESTINATION_OVER, drawingProvider.getGlobalCompositeOperation());
  });
});