diff --git a/src/main/color/palette/palette-color-selector.ts b/src/main/color/palette/palette-color-selector.ts index 5e2c75b..27bbb2c 100644 --- a/src/main/color/palette/palette-color-selector.ts +++ b/src/main/color/palette/palette-color-selector.ts @@ -27,9 +27,6 @@ const p5: P5Lib = SketchContext.p5; // TODO - documentation // TODO - update release notes export class PaletteColorSelector extends ColorSelector { - // TODO - documentation - private static readonly _MIN_COLOR_COUNT: number = 1; - // TODO - documentation private readonly _NAME: string; @@ -45,8 +42,8 @@ export class PaletteColorSelector extends ColorSelector { this._NAME = this.buildName(palette); - let count: number = colorCount ?? Random.randomInt(PaletteColorSelector._MIN_COLOR_COUNT, palette.COLORS.length + 1); - count = p5.constrain(count, PaletteColorSelector._MIN_COLOR_COUNT, palette.COLORS.length); + let count: number = colorCount ?? Random.randomInt(PaletteColorSelector.MIN_COLOR_COUNT, palette.COLORS.length + 1); + count = p5.constrain(count, PaletteColorSelector.MIN_COLOR_COUNT, palette.COLORS.length); const paletteOrder: boolean = buildWithPaletteOrder ?? Random.randomBoolean(); @@ -78,7 +75,11 @@ export class PaletteColorSelector extends ColorSelector { } // TODO - documentation - // TODO - unit test - does color selector have the right name? + private static get MIN_COLOR_COUNT(): number { + return 1; + } + + // TODO - documentation private buildName(palette: Palette): string { let paletteName: string = palette.NAME.toLowerCase(); @@ -92,24 +93,26 @@ export class PaletteColorSelector extends ColorSelector { // TODO - documentation private choosePaletteColors(palette: Palette, buildWithPaletteOrder: boolean, colorCount: number): void { - colorCount = p5.constrain(colorCount, PaletteColorSelector._MIN_COLOR_COUNT, palette.COLORS.length); + colorCount = p5.constrain(colorCount, PaletteColorSelector.MIN_COLOR_COUNT, palette.COLORS.length); - if (buildWithPaletteOrder) { - for (let i: number = 0; i < colorCount; i++) { - const pc: PaletteColor = palette.COLORS[i]; - this.addColorChoice(new Color(pc)); - this._COLOR_NAMES.push(pc.NAME); - } - } else { - const selector: RandomSelector = new RandomSelector(palette.COLORS); - - for (let i: number = 0; i < colorCount; i++) { - const pc: PaletteColor | undefined = selector.getRandomElementAndRemove(); - - if (pc) { + if (palette.COLORS.length > 0) { + if (buildWithPaletteOrder) { + for (let i: number = 0; i < colorCount; i++) { + const pc: PaletteColor = palette.COLORS[i]; this.addColorChoice(new Color(pc)); this._COLOR_NAMES.push(pc.NAME); } + } else { + const selector: RandomSelector = new RandomSelector(palette.COLORS); + + for (let i: number = 0; i < colorCount; i++) { + const pc: PaletteColor | undefined = selector.getRandomElementAndRemove(); + + if (pc) { + this.addColorChoice(new Color(pc)); + this._COLOR_NAMES.push(pc.NAME); + } + } } } } diff --git a/src/test/color/color-selector.test.ts b/src/test/color/color-selector.test.ts index 419bb8d..06cfc04 100644 --- a/src/test/color/color-selector.test.ts +++ b/src/test/color/color-selector.test.ts @@ -15,129 +15,106 @@ * See the GNU Affero General Public License for more details. */ -import {Color, ColorSelector, DefaultColorSelector} from 'color'; -import {StringMap} from 'map'; +import {Color, DefaultColorSelector} from 'color'; + +import { + SampleSelector, + blue, + cyan, + green, + red, + checkForValidColorSelector, + checkForValidInOrderSelector, + checkForValidRandomSelector +} from 'unit-test/shared'; -import {blue, ColorComponents, colorToColorComponents, cyan, green, red, SampleSelector} from '../shared/color'; -describe('color selector tests', (): void => { - function checkForValidColorSelector(selector: ColorSelector): void { - expect(selector.type).toBeTruthy(); - expect(selector.name).toBeTruthy(); - expect(selector.colorNames).toBeTruthy(); - expect(selector.colorNames.length).toBeGreaterThan(0); - } - - function checkForValidInOrderSelector(selector: ColorSelector, colors: Color[]): void { - for (let i: number = 0; i < colors.length * 2; i++) { - const selectedColor: Color = selector.getColor(); - const selectedComps: ColorComponents = colorToColorComponents(selectedColor); - const expectedComps: ColorComponents = colorToColorComponents(colors[i % colors.length]); - expect(selectedComps).toEqual(expectedComps); - } - } - - function checkForValidRandomSelector(selector: ColorSelector, colors: Color[]): void { - const expectedComponents: ColorComponents[] = colors.map((c: Color): ColorComponents => colorToColorComponents(c)); - const colorMap: StringMap = new StringMap(); - - for (let i: number = 0; i < 25; i++) { - const components: ColorComponents = colorToColorComponents(selector.getColor()); - const key: string = components.r.toString() + '' + components.g.toString() + '' + components.b.toString(); - colorMap.setUndefinedKey(key, components); - } - - expect(colorMap.size).toEqual(expectedComponents.length); - - for (const components of colorMap.values) { - expect(expectedComponents).toContainEqual(components); - } - } +describe('color selector tests', (): void => { test('test default color selector', (): void => { const selector: DefaultColorSelector = new DefaultColorSelector(); checkForValidColorSelector(selector); - checkForValidInOrderSelector(selector, [new Color()]); + checkForValidInOrderSelector(selector, [new Color()], true, true); }); test('color selector test: no colors; in order', (): void => { const selector: SampleSelector = new SampleSelector([], false); checkForValidColorSelector(selector); - checkForValidInOrderSelector(selector, [new Color()]); + checkForValidInOrderSelector(selector, [new Color()], true, true); }); test('color selector test: no colors; random order', (): void => { const selector: SampleSelector = new SampleSelector([], true); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, [new Color()]); + checkForValidRandomSelector(selector, [new Color()], true); }); test('color selector test: no order provided', (): void => { const selector: SampleSelector = new SampleSelector([]); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, [new Color()]); + checkForValidRandomSelector(selector, [new Color()], true); }); test('color selector test: >2 colors; in order', (): void => { const colors: Color[] = [red, green, blue, cyan]; const selector: SampleSelector = new SampleSelector(colors, false); checkForValidColorSelector(selector); - checkForValidInOrderSelector(selector, colors); + checkForValidInOrderSelector(selector, colors, true, true); }); test('color selector test: >2 colors; random order', (): void => { const colors: Color[] = [red, green, blue, cyan]; const selector: SampleSelector = new SampleSelector(colors, true); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, colors); + checkForValidRandomSelector(selector, colors, true); }); test('color selector test: >2 colors; no order provided', (): void => { const colors: Color[] = [red, green, blue, cyan]; const selector: SampleSelector = new SampleSelector(colors); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, colors); + checkForValidRandomSelector(selector, colors, true); }); test('color selector test: 2 colors; in order', (): void => { const colors: Color[] = [red, green]; const selector: SampleSelector = new SampleSelector(colors, false); checkForValidColorSelector(selector); - checkForValidInOrderSelector(selector, colors); + checkForValidInOrderSelector(selector, colors, true, true); }); test('color selector test: 2 colors; random order', (): void => { const colors: Color[] = [red, green]; const selector: SampleSelector = new SampleSelector(colors, true); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, colors); + checkForValidRandomSelector(selector, colors, true); }); test('color selector test: 2 colors; no order provided', (): void => { const colors: Color[] = [red, green]; const selector: SampleSelector = new SampleSelector(colors); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, colors); + checkForValidRandomSelector(selector, colors, true); }); test('color selector test: 1 color; in order', (): void => { const colors: Color[] = [red]; const selector: SampleSelector = new SampleSelector(colors, false); checkForValidColorSelector(selector); - checkForValidInOrderSelector(selector, colors); + checkForValidInOrderSelector(selector, colors, true, true); }); test('color selector test: 1 color; random order', (): void => { const colors: Color[] = [red]; const selector: SampleSelector = new SampleSelector(colors, true); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, colors); + checkForValidRandomSelector(selector, colors, true); }); test('color selector test: 1 color; no order provided', (): void => { const colors: Color[] = [red]; const selector: SampleSelector = new SampleSelector(colors); checkForValidColorSelector(selector); - checkForValidRandomSelector(selector, colors); + checkForValidRandomSelector(selector, colors, true); }); }); diff --git a/src/test/color/palette/palette-color-selector.test.ts b/src/test/color/palette/palette-color-selector.test.ts new file mode 100644 index 0000000..4212a6a --- /dev/null +++ b/src/test/color/palette/palette-color-selector.test.ts @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2024 brittni and the polar bear LLC. + * + * This file is a part of brittni and the polar bear's Generative Art Library, + * which is released under the GNU Affero General Public License, Version 3.0. + * You may not use this file except in compliance with the license. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. See LICENSE or go to + * https://www.gnu.org/licenses/agpl-3.0.en.html for full license details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + */ + +import {Palette, PaletteColorSelector} from 'palette'; +import {_0437F2, _0FFF4F, _121212, _7A00F5, _FF6BB5} from 'palette-colors'; +import {Discriminators} from 'discriminator'; +import { + checkForValidColorSelector, + checkForValidInOrderSelector, + checkForValidRandomSelector, + getColorsArray +} from "unit-test/shared"; + +// palette order, some colors, random +// palette order, 2 colors, not random +// palette order, 2 colors, random +// palette order, 1 color, not random +// palette order, 1 color, random + +// not palette order, all colors, not random +// not palette order, all colors, random +// not palette order, some colors, not random +// not palette order, some colors, random +// not palette order, 2 colors, not random +// not palette order, 2 colors, random +// not palette order, 1 color, not random +// not palette order, 1 color, random + +const TEST_PALETTE_A: Palette = { + NAME: 'test A palette', + SOURCE: 'test a source', + COLORS: [ + _7A00F5, + _121212, + _0437F2, + _FF6BB5, + _0FFF4F + ], + CONTRAST_MAP: { + '#000000': [], + '#FFFFFF': [] + }, + IS_GRADIENT: false, + DISCRIMINATOR: Discriminators.PALETTE +}; + +const TEST_PALETTE_B: Palette = { + NAME: 'test B', + SOURCE: 'test b source', + COLORS: [], + CONTRAST_MAP: { + '#000000': [], + '#FFFFFF': [] + }, + IS_GRADIENT: false, + DISCRIMINATOR: Discriminators.PALETTE +}; + +describe('palette color selector tests', (): void => { + test('test palette color selector names', (): void => { + const selectorA: PaletteColorSelector = new PaletteColorSelector(TEST_PALETTE_A); + expect(selectorA).toBeTruthy(); + expect(selectorA.name).toBe('test a palette color selector'); + + const selectorB: PaletteColorSelector = new PaletteColorSelector(TEST_PALETTE_B); + expect(selectorB).toBeTruthy(); + expect(selectorB.name).toBe('test b palette color selector'); + }); + + test('test palette color selector: palette only', (): void => { + const selector: PaletteColorSelector = new PaletteColorSelector(TEST_PALETTE_A); + expect(selector).toBeTruthy(); + checkForValidColorSelector(selector); + checkForValidRandomSelector(selector, getColorsArray(TEST_PALETTE_A.COLORS), false); + }); + + test('test palette color selector: palette order build, all colors, not random order', (): void => { + const selector: PaletteColorSelector = + new PaletteColorSelector( + TEST_PALETTE_A, + true, + TEST_PALETTE_A.COLORS.length, + false + ); + expect(selector).toBeTruthy(); + checkForValidColorSelector(selector); + checkForValidInOrderSelector( + selector, + getColorsArray(TEST_PALETTE_A.COLORS), + true, + true + ); + }); + + // palette order, all colors, random + test('test palette color selector: palette order build, all colors, random order', (): void => { + const selector: PaletteColorSelector = + new PaletteColorSelector( + TEST_PALETTE_A, + true, + TEST_PALETTE_A.COLORS.length, + true + ); + expect(selector).toBeTruthy(); + checkForValidColorSelector(selector); + checkForValidRandomSelector(selector,getColorsArray(TEST_PALETTE_A.COLORS), true); + }); + + test('test palette color selector: palette order build, 3 colors, not random order', (): void => { + const colorCount: number = 3; + const selector: PaletteColorSelector = + new PaletteColorSelector( + TEST_PALETTE_A, + true, + colorCount, + false + ); + expect(selector).toBeTruthy(); + checkForValidColorSelector(selector); + checkForValidInOrderSelector( + selector, + getColorsArray(TEST_PALETTE_A.COLORS), + true, + false, + colorCount + ); + }); +}); diff --git a/src/test/shared/color-selector.ts b/src/test/shared/color-selector.ts new file mode 100644 index 0000000..5209446 --- /dev/null +++ b/src/test/shared/color-selector.ts @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 brittni and the polar bear LLC. + * + * This file is a part of brittni and the polar bear's Generative Art Library, + * which is released under the GNU Affero General Public License, Version 3.0. + * You may not use this file except in compliance with the license. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. See LICENSE or go to + * https://www.gnu.org/licenses/agpl-3.0.en.html for full license details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + */ + +import {Color, ColorSelector} from 'color'; +import {StringMap} from 'map'; + +import {ColorComponents, colorToColorComponents} from './color'; + +export function checkForValidColorSelector(selector: ColorSelector): void { + expect(selector.type).toBeTruthy(); + expect(selector.name).toBeTruthy(); + expect(selector.colorNames).toBeTruthy(); + expect(selector.colorNames.length).toBeGreaterThan(0); +} + +export function checkForValidRandomSelector(selector: ColorSelector, + colors: Color[], + picksAllColors: boolean, + colorCount?: number): void { + checkForValidColorSelector(selector); + const expectedComponents: ColorComponents[] = colors.map((c: Color): ColorComponents => colorToColorComponents(c)); + const colorMap: StringMap = new StringMap(); + + for (let i: number = 0; i < 25; i++) { + const components: ColorComponents = colorToColorComponents(selector.getColor()); + const key: string = components.r.toString() + '' + components.g.toString() + '' + components.b.toString(); + colorMap.setUndefinedKey(key, components); + } + + if (picksAllColors) { + expect(colorMap.size).toEqual(expectedComponents.length); + } else if (colorCount) { + expect(colorMap.size).toEqual(colorCount); + } + + for (const components of colorMap.values) { + expect(expectedComponents).toContainEqual(components); + } +} + +export function checkForValidInOrderSelector(selector: ColorSelector, + colors: Color[], + matchColorOrder: boolean, + picksAllColors: boolean, + colorCount?: number): void { + checkForValidColorSelector(selector); + + if (!colorCount || colorCount < 0) { + colorCount = colors.length; + } + + if (matchColorOrder && !picksAllColors) { + colors = colors.slice(0, colorCount); + } + + const expectedComponents: ColorComponents[] = colors.map((c: Color): ColorComponents => colorToColorComponents(c)); + const colorMap: StringMap = new StringMap(); + + if (!matchColorOrder) { + const inOrderColors: Color[] = []; + + for (let i: number = 0; i < colorCount; i++) { + const c: Color = selector.getColor(); + const components: ColorComponents = colorToColorComponents(c); + expect(expectedComponents).toContainEqual(components); + inOrderColors.push(c); + + const key: string = components.r.toString() + '' + components.g.toString() + '' + components.b.toString(); + colorMap.setUndefinedKey(key, components); + } + + for (let i: number = 0; i < colorCount; i++) { + const c: Color = selector.getColor(); + const components: ColorComponents = colorToColorComponents(c); + const expected: Color = inOrderColors[i]; + const expectedComponents: ColorComponents = colorToColorComponents(expected); + expect(components).toEqual(expectedComponents); + } + } else { + for (let i: number = 0; i < colors.length * 2; i++) { + const selectedColor: Color = selector.getColor(); + const selectedComps: ColorComponents = colorToColorComponents(selectedColor); + const expectedComps: ColorComponents = colorToColorComponents(colors[i % colors.length]); + expect(selectedComps).toEqual(expectedComps); + + const key: string = selectedComps.r.toString() + '' + selectedComps.g.toString() + '' + selectedComps.b.toString(); + colorMap.setUndefinedKey(key, selectedComps); + } + } + + if (picksAllColors) { + expect(colorMap.size).toEqual(expectedComponents.length); + } else if (colorCount) { + expect(colorMap.size).toEqual(colorCount); + } + + for (const components of colorMap.values) { + expect(expectedComponents).toContainEqual(components); + } +} diff --git a/src/test/shared/index.ts b/src/test/shared/index.ts index da9bf78..f42314b 100644 --- a/src/test/shared/index.ts +++ b/src/test/shared/index.ts @@ -16,6 +16,7 @@ */ export * from './color'; +export * from './color-selector'; export * from './map'; export * from './math'; export * from './palette'; diff --git a/src/test/shared/palette.ts b/src/test/shared/palette.ts index 496c753..74c82b8 100644 --- a/src/test/shared/palette.ts +++ b/src/test/shared/palette.ts @@ -15,13 +15,20 @@ * See the GNU Affero General Public License for more details. */ +import {Color} from 'color'; +import {ColorContrastAssessor, ContrastFontSize, ContrastStandard} from 'color-contrast'; import {StringMap} from 'map'; import {Palette, PaletteColor} from 'palette'; import {checkForValidHexColorString, ColorComponents} from './color'; import {checkForValidStringMap} from './map'; import {checkNumberWithinAmount} from './math'; -import {ColorContrastAssessor, ContrastFontSize, ContrastStandard} from "color-contrast"; + +export function getColorsArray(colors: PaletteColor[]): Color[] { + return colors.map((color: PaletteColor) => { + return new Color(color); + }); +} export function checkComponents(actual: ColorComponents, expected: PaletteColor): void { checkNumberWithinAmount(actual.r, expected.RGB.R, 1); @@ -67,12 +74,12 @@ export function checkForValidContrastMap(palette: Palette): void { return color.HEX; }); - if (validHexes.indexOf('#000000') < 0) { + if (!validHexes.includes('#000000')) { expect(palette.CONTRAST_MAP['#FFFFFF']).not.toContain('#000000'); validHexes.push('#000000'); } - if (validHexes.indexOf('#FFFFFF') < 0) { + if (!validHexes.includes('#FFFFFF')) { expect(palette.CONTRAST_MAP['#000000']).not.toContain('#FFFFFF'); validHexes.push('#FFFFFF'); }