assert#strictEqual TypeScript Examples

The following examples show how to use assert#strictEqual. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: mempool.test.ts    From foundry-rpc-js with ISC License 5 votes vote down vote up
function randomHexString(length: number): string {
    strictEqual(length % 2, 0);
    const buffer = Buffer.alloc(length / 2);
    for (let i = 0; i < length / 2; i += 1) {
        buffer[i] = Math.floor(Math.random() * 256);
    }
    return buffer.toString("hex");
}
Example #2
Source File: test-commons.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
export function assertContent(actual: string, expect: string, multiline: boolean = true) {
  assert.strictEqual(drain(actual, multiline), drain(expect, multiline));
}
Example #3
Source File: test-commons.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
export function assertFilesPlain(filePath: string, clientDir: string, fixturesDir: string) {
  const actual = fs.readFileSync(path.join(clientDir, filePath), 'utf8');
  const expect = fs.readFileSync(path.join(fixturesDir, filePath), 'utf8');
  strictEqual(actual, expect);
}
Example #4
Source File: login.ts    From cli with Apache License 2.0 5 votes vote down vote up
async function startLoginFlow(browser: Browser) {
  const username = process.env.PLATFORM_USER_NAME;
  const password = process.env.PLATFORM_USER_PASSWORD;

  if (!username || !password) {
    throw new Error('Missing login credentials');
  }

  const page = await getLoginPage(browser);

  await page.waitForSelector(LoginSelectors.loginWithOfficeButton, {
    visible: true,
  });

  await Promise.all([
    page.click(LoginSelectors.loginWithOfficeButton),
    page.waitForNavigation({waitUntil: 'networkidle2'}),
    page.waitForSelector(LoginSelectors.emailView),
  ]);

  await page.waitForSelector(LoginSelectors.emailInput, {
    visible: true,
  });
  await page.type(LoginSelectors.emailInput, username);
  await page.waitForSelector(LoginSelectors.SubmitInput, {
    visible: true,
  });
  await Promise.all([
    page.click(LoginSelectors.SubmitInput),
    page.waitForSelector(LoginSelectors.passwordView),
  ]);

  await page.waitForSelector(LoginSelectors.passwordInput, {
    visible: true,
  });
  await page.type(LoginSelectors.passwordInput, password);
  await page.waitForSelector(LoginSelectors.SubmitInput, {
    visible: true,
  });
  await Promise.all([
    page.click(`${LoginSelectors.passwordView} ${LoginSelectors.SubmitInput}`),
    page.waitForNavigation({waitUntil: 'networkidle2', timeout: 2 * 60e3}),
  ]);

  await staySignedIn(page);

  await possiblyAcceptCustomerAgreement(page);

  await retry(async () => strictEqual(await isLoggedin(), true));

  if ((await browser.pages()).length < 2) {
    await browser.newPage();
  }

  await page.close();
}
Example #5
Source File: artboard.spec.ts    From open-design-sdk with Apache License 2.0 4 votes vote down vote up
describe('Artboard', () => {
  function createOctopus<T extends Partial<OctopusDocument>>(
    data: T
  ): OctopusDocument {
    return {
      'frame': {
        'x': Math.round(Math.random() * 400),
        'y': Math.round(Math.random() * 400),
      },
      'bounds': {
        'left': 0,
        'top': 0,
        'width': Math.round(Math.random() * 400),
        'height': Math.round(Math.random() * 400),
      },
      'layers': [],
      ...data,
    }
  }

  function createLayerOctopus<T extends Partial<LayerOctopusData>>(
    data: T
  ): LayerOctopusData {
    const id = String(Math.round(Math.random() * 400))
    const base = {
      'id': `layer-${id}`,
      'name': `Layer ID=${id}`,
      'type': 'layer',
    }
    const type = data['type'] || base['type']

    // @ts-expect-error Hard to generalize.
    return {
      ...base,
      ...(type === 'textLayer' ? { 'text:': {} } : {}),
      ...(type === 'groupLayer' ? { 'layers:': [] } : {}),
      ...data,
    }
  }

  describe('artboard info', () => {
    it('should have the provided ID', () => {
      const artboard = new Artboard('a', createOctopus({}))

      strictEqual(artboard.id, 'a')
    })

    it('should have the provided octopus', () => {
      const octopus = createOctopus({})
      const artboard = new Artboard('a', { ...octopus })

      deepStrictEqual(artboard.getOctopus(), octopus)
    })

    it('should have the provided name', () => {
      const artboard = new Artboard('a', createOctopus({}), { name: 'Abc' })

      strictEqual(artboard.name, 'Abc')
    })

    it('should not have a name by default', () => {
      const artboard = new Artboard('a', createOctopus({}))

      strictEqual(artboard.name, null)
    })

    it('should have the provided page ID', () => {
      const artboard = new Artboard('a', createOctopus({}), { pageId: 'p1' })

      strictEqual(artboard.pageId, 'p1')
    })

    it('should not have a page ID by default', () => {
      const artboard = new Artboard('a', createOctopus({}))

      strictEqual(artboard.pageId, null)
    })

    it('should return the provided design', () => {
      const design = new Design()
      const artboard = new Artboard('a', createOctopus({}), { design })

      strictEqual(artboard.getDesign(), design)
    })
  })

  describe('layer lists', () => {
    it('should return a root layer list based on layers in octopus', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({ 'id': 'x' }),
          createLayerOctopus({ 'id': 'a' }),
          createLayerOctopus({ 'id': 'o' }),
        ],
      })
      const artboard = new Artboard('a', octopus)

      const rootLayers = artboard.getRootLayers().getLayers()

      strictEqual(rootLayers.length, 3)
      ok(rootLayers[0])
      ok(rootLayers[1])
      ok(rootLayers[2])
      strictEqual(rootLayers[0].id, 'x')
      strictEqual(rootLayers[1].id, 'a')
      strictEqual(rootLayers[2].id, 'o')
    })

    it('should return the same root layer list on multiple getter calls', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({}),
          createLayerOctopus({}),
          createLayerOctopus({}),
        ],
      })
      const artboard = new Artboard('a', octopus)

      const rootLayers1 = artboard.getRootLayers()
      const rootLayers2 = artboard.getRootLayers()
      strictEqual(rootLayers1, rootLayers2)
    })

    it('should return a flattened layer list based on layers in octopus', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({ 'id': 'a' }),
          createLayerOctopus({
            'id': 'b',
            'type': 'groupLayer',
            'layers': [createLayerOctopus({ 'id': 'a2' })],
          }),
          createLayerOctopus({
            'id': 'c',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({ 'id': 'b2' }),
              createLayerOctopus({ 'id': 'c2' }),
            ],
          }),
        ],
      })
      const artboard = new Artboard('a', octopus)

      const flattenedLayers = artboard.getFlattenedLayers().getLayers()

      strictEqual(flattenedLayers.length, 6)
      strictEqual(flattenedLayers[0]?.id, 'a')
      strictEqual(flattenedLayers[1]?.id, 'b')
      strictEqual(flattenedLayers[2]?.id, 'a2')
      strictEqual(flattenedLayers[3]?.id, 'c')
      strictEqual(flattenedLayers[4]?.id, 'b2')
      strictEqual(flattenedLayers[5]?.id, 'c2')
    })

    it('should return the same flattened layer list on multiple getter calls', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({ 'id': 'a' }),
          createLayerOctopus({
            'id': 'b',
            'type': 'groupLayer',
            'layers': [createLayerOctopus({ 'id': 'c' })],
          }),
        ],
      })
      const artboard = new Artboard('a', octopus)

      const flattenedLayers1 = artboard.getFlattenedLayers()
      const flattenedLayers2 = artboard.getFlattenedLayers()
      strictEqual(flattenedLayers1, flattenedLayers2)
    })
  })

  describe('layer lookup', () => {
    describe('ID-based layer lookup', () => {
      it('should look up a root layer by ID', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a' }),
            createLayerOctopus({ 'id': 'b' }),
            createLayerOctopus({ 'id': 'c' }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(artboard.getLayerById('b')?.id, 'b')
      })

      it('should look up a first-level nested layer by ID', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a' }),
            createLayerOctopus({
              'id': 'b',
              'type': 'groupLayer',
              'layers': [createLayerOctopus({ 'id': 'c' })],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(artboard.getLayerById('c')?.id, 'c')
      })

      it('should look up a deeper-level nested layer by ID', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'layers': [
                createLayerOctopus({
                  'id': 'b',
                  'type': 'groupLayer',
                  'layers': [createLayerOctopus({ 'id': 'c' })],
                }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(artboard.getLayerById('c')?.id, 'c')
      })
    })

    describe('selector-based individual layer lookup', () => {
      it('should look up a root layer by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
            createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(artboard.findLayer({ name: 'Layer B' })?.id, 'b')
      })

      it('should look up a first-level nested layer by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'type': 'groupLayer',
              'layers': [createLayerOctopus({ 'id': 'c', 'name': 'Layer C' })],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(artboard.findLayer({ name: 'Layer C' })?.id, 'c')
      })

      it('should look up a deeper-level nested layer by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'name': 'Layer A',
              'layers': [
                createLayerOctopus({
                  'id': 'b',
                  'name': 'Layer B',
                  'type': 'groupLayer',
                  'layers': [
                    createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                  ],
                }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(artboard.findLayer({ name: 'Layer C' })?.id, 'c')
      })

      it('should look up a root layer by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
            createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(
          artboard.findLayer({ name: ['Layer C', 'Layer B'] })?.id,
          'b'
        )
      })

      it('should look up a first-level nested layer by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'type': 'groupLayer',
              'layers': [
                createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(
          artboard.findLayer({ name: ['Layer D', 'Layer C'] })?.id,
          'c'
        )
      })

      it('should look up a deeper-level nested layer by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'name': 'Layer A',
              'layers': [
                createLayerOctopus({
                  'id': 'b',
                  'name': 'Layer B',
                  'type': 'groupLayer',
                  'layers': [
                    createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                    createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                  ],
                }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        strictEqual(
          artboard.findLayer({ name: ['Layer D', 'Layer C'] })?.id,
          'c'
        )
      })
    })

    describe('batch selector-based layer lookup', () => {
      it('should look up a root layer by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
            createLayerOctopus({ 'id': 'c', 'name': 'Layer A' }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersA = artboard.findLayers({ name: 'Layer A' })
        strictEqual(layersA.length, 2)
        strictEqual(layersA.getLayerById('a')?.id, 'a')
        strictEqual(layersA.getLayerById('c')?.id, 'c')
      })

      it('should look up first-level nested layers by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'type': 'groupLayer',
              'layers': [
                createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                createLayerOctopus({ 'id': 'e', 'name': 'Layer C' }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersC = artboard.findLayers({ name: 'Layer C' })
        strictEqual(layersC.length, 2)
        strictEqual(layersC.getLayerById('c')?.id, 'c')
        strictEqual(layersC.getLayerById('e')?.id, 'e')
      })

      it('should look up a deeper-level nested layer by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'name': 'Layer A',
              'layers': [
                createLayerOctopus({
                  'id': 'b',
                  'name': 'Layer B',
                  'type': 'groupLayer',
                  'layers': [
                    createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                    createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                    createLayerOctopus({ 'id': 'e', 'name': 'Layer C' }),
                  ],
                }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersC = artboard.findLayers({ name: 'Layer C' })
        strictEqual(layersC.length, 2)
        strictEqual(layersC.getLayerById('c')?.id, 'c')
        strictEqual(layersC.getLayerById('e')?.id, 'e')
      })

      it('should look up root layers by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
            createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersAC = artboard.findLayers({ name: ['Layer C', 'Layer A'] })
        strictEqual(layersAC.length, 2)
        strictEqual(layersAC.getLayerById('a')?.id, 'a')
        strictEqual(layersAC.getLayerById('c')?.id, 'c')
      })

      it('should look up first-level nested layers by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'type': 'groupLayer',
              'layers': [
                createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                createLayerOctopus({ 'id': 'e', 'name': 'Layer E' }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersCD = artboard.findLayers({ name: ['Layer D', 'Layer C'] })
        strictEqual(layersCD.length, 2)
        strictEqual(layersCD.getLayerById('c')?.id, 'c')
        strictEqual(layersCD.getLayerById('d')?.id, 'd')
      })

      it('should look up deeper-level nested layers by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'name': 'Layer A',
              'layers': [
                createLayerOctopus({
                  'id': 'b',
                  'name': 'Layer B',
                  'type': 'groupLayer',
                  'layers': [
                    createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                    createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                    createLayerOctopus({ 'id': 'e', 'name': 'Layer E' }),
                  ],
                }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersCD = artboard.findLayers({ name: ['Layer D', 'Layer C'] })
        strictEqual(layersCD.length, 2)
        strictEqual(layersCD.getLayerById('c')?.id, 'c')
        strictEqual(layersCD.getLayerById('d')?.id, 'd')
      })

      it('should not return duplicate results when both a group layer and layers nested within it are matched', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'name': 'Layer A',
              'layers': [
                createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
                createLayerOctopus({ 'id': 'c', 'name': 'Layer A' }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersABC = artboard.findLayers({ id: ['a', 'b', 'c'] })
        strictEqual(layersABC.length, 3)
        strictEqual(layersABC.getLayerById('a')?.id, 'a')
        strictEqual(layersABC.getLayerById('b')?.id, 'b')
        strictEqual(layersABC.getLayerById('c')?.id, 'c')
      })
    })

    describe('batch bitmap asset name selector-based layer lookup', () => {
      it('should look up a root layer by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
            createLayerOctopus({ 'id': 'c', 'name': 'Layer A' }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersA = artboard.findLayers({ name: 'Layer A' })
        strictEqual(layersA.length, 2)
        strictEqual(layersA.getLayerById('a')?.id, 'a')
        strictEqual(layersA.getLayerById('c')?.id, 'c')
      })

      it('should look up first-level nested layers by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'type': 'groupLayer',
              'layers': [
                createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                createLayerOctopus({ 'id': 'e', 'name': 'Layer C' }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersC = artboard.findLayers({ name: 'Layer C' })
        strictEqual(layersC.length, 2)
        strictEqual(layersC.getLayerById('c')?.id, 'c')
        strictEqual(layersC.getLayerById('e')?.id, 'e')
      })

      it('should look up a deeper-level nested layer by an exact match selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'name': 'Layer A',
              'layers': [
                createLayerOctopus({
                  'id': 'b',
                  'name': 'Layer B',
                  'type': 'groupLayer',
                  'layers': [
                    createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                    createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                    createLayerOctopus({ 'id': 'e', 'name': 'Layer C' }),
                  ],
                }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersC = artboard.findLayers({ name: 'Layer C' })
        strictEqual(layersC.length, 2)
        strictEqual(layersC.getLayerById('c')?.id, 'c')
        strictEqual(layersC.getLayerById('e')?.id, 'e')
      })

      it('should look up root layers by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
            createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersAC = artboard.findLayers({ name: ['Layer C', 'Layer A'] })
        strictEqual(layersAC.length, 2)
        strictEqual(layersAC.getLayerById('a')?.id, 'a')
        strictEqual(layersAC.getLayerById('c')?.id, 'c')
      })

      it('should look up first-level nested layers by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'type': 'groupLayer',
              'layers': [
                createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                createLayerOctopus({ 'id': 'e', 'name': 'Layer E' }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersCD = artboard.findLayers({ name: ['Layer D', 'Layer C'] })
        strictEqual(layersCD.length, 2)
        strictEqual(layersCD.getLayerById('c')?.id, 'c')
        strictEqual(layersCD.getLayerById('d')?.id, 'd')
      })

      it('should look up deeper-level nested layers by a list selector', () => {
        const octopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'a',
              'name': 'Layer A',
              'layers': [
                createLayerOctopus({
                  'id': 'b',
                  'name': 'Layer B',
                  'type': 'groupLayer',
                  'layers': [
                    createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                    createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                    createLayerOctopus({ 'id': 'e', 'name': 'Layer E' }),
                  ],
                }),
              ],
            }),
          ],
        })
        const artboard = new Artboard('a', octopus)

        const layersCD = artboard.findLayers({ name: ['Layer D', 'Layer C'] })
        strictEqual(layersCD.length, 2)
        strictEqual(layersCD.getLayerById('c')?.id, 'c')
        strictEqual(layersCD.getLayerById('d')?.id, 'd')
      })
    })
  })

  describe('layer collection flattening', () => {
    it('should not return duplicate results when both a group layer and layers nested within it are matched', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({
            'id': 'a',
            'name': 'Layer A',
            'layers': [
              createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
              createLayerOctopus({ 'id': 'c', 'name': 'Layer A' }),
            ],
          }),
        ],
      })
      const artboard = new Artboard('a', octopus)

      const layersABC = artboard.findLayers({ id: ['a', 'b', 'c'] })
      const flattened = layersABC.flatten()
      strictEqual(flattened.length, 3)
      strictEqual(flattened.getLayerById('a')?.id, 'a')
      strictEqual(flattened.getLayerById('b')?.id, 'b')
      strictEqual(flattened.getLayerById('c')?.id, 'c')
    })
  })

  describe('layer depth info', () => {
    it('should return the depth of 0 for root layers', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
          createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
          createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
        ],
      })
      const artboard = new Artboard('a', octopus)

      strictEqual(artboard.getLayerDepth('b'), 0)
    })

    it('should return the depth of 1 for first-level nested layers', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({ 'id': 'a', 'name': 'Layer A' }),
          createLayerOctopus({
            'id': 'b',
            'name': 'Layer B',
            'type': 'groupLayer',
            'layers': [createLayerOctopus({ 'id': 'c', 'name': 'Layer C' })],
          }),
        ],
      })
      const artboard = new Artboard('a', octopus)

      strictEqual(artboard.getLayerDepth('c'), 1)
    })

    it('should return depth for deeper-level nested layers', () => {
      const octopus = createOctopus({
        'layers': [
          createLayerOctopus({
            'id': 'a',
            'name': 'Layer A',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'name': 'Layer B',
                'type': 'groupLayer',
                'layers': [
                  createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                ],
              }),
            ],
          }),
        ],
      })
      const artboard = new Artboard('a', octopus)

      strictEqual(artboard.getLayerDepth('c'), 2)
    })
  })

  describe('artboard background', () => {
    it('should not return a background color when no color is specified in octopus', () => {
      const octopus = createOctopus({
        'hasBackgroundColor': false,
        'backgroundColor': undefined,
      })
      const artboard = new Artboard('a', octopus)

      strictEqual(artboard.getBackgroundColor(), null)
    })

    it('should not return the background color from octopus when the background color is disabled', () => {
      const octopus = createOctopus({
        'hasBackgroundColor': false,
        'backgroundColor': { 'r': 50, 'g': 150, 'b': 250, 'a': 1 },
      })
      const artboard = new Artboard('a', octopus)

      strictEqual(artboard.getBackgroundColor(), null)
    })

    it('should return the background color from octopus when the background color is enabled', () => {
      const octopus = createOctopus({
        'hasBackgroundColor': true,
        'backgroundColor': { 'r': 50, 'g': 150, 'b': 250, 'a': 1 },
      })
      const artboard = new Artboard('a', octopus)

      deepStrictEqual(artboard.getBackgroundColor(), {
        'r': 50,
        'g': 150,
        'b': 250,
        'a': 1,
      })
    })
  })

  describe('component artboards', () => {
    it('should claim the artboard is a component when it has a symbol ID in octopus', () => {
      const octopus = createOctopus({
        'symbolID': 'x',
      })
      const artboard = new Artboard('a', octopus)

      strictEqual(artboard.isComponent(), true)
    })

    it('should not claim the artboard is a component when it does not have a symbol ID in octopus', () => {
      const octopus = createOctopus({
        'symbolID': undefined,
      })
      const artboard = new Artboard('a', octopus)

      strictEqual(artboard.isComponent(), false)
    })
  })
})
Example #6
Source File: design.spec.ts    From open-design-sdk with Apache License 2.0 4 votes vote down vote up
describe('Design', () => {
  function createOctopus() {
    const width = Math.round(Math.random() * 400)
    const height = Math.round(Math.random() * 400)

    return {
      'frame': {
        'x': Math.round(Math.random() * 400),
        'y': Math.round(Math.random() * 400),
      },
      'bounds': {
        'left': 0,
        'top': 0,
        'right': width,
        'bottom': height,
        'width': width,
        'height': height,
      },
      'layers': [],
    }
  }

  it('should return an added artboard by ID', () => {
    const design = new Design()

    design.addArtboard('a', createOctopus())

    const artboard = design.getArtboardById('a')
    ok(artboard)
    strictEqual(artboard.id, 'a')
  })

  it('should not return a removed artboard by ID', () => {
    const design = new Design()

    design.addArtboard('a', createOctopus())
    design.removeArtboard('a')

    const artboard = design.getArtboardById('a')
    strictEqual(artboard, null)
  })

  describe('artboard list', () => {
    it('should return an empty artboard list by default', () => {
      const design = new Design()

      strictEqual(design.getArtboards().length, 0)
    })

    it('should include an added artboard in the artboard list', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus())

      const artboards = design.getArtboards()
      strictEqual(artboards.length, 1)
      ok(artboards[0])
      strictEqual(artboards[0].id, 'a')
    })

    it('should not include newly added artboards in a previously returned artboard list', () => {
      const design = new Design()

      const prevArtboards = design.getArtboards()

      design.addArtboard('a', createOctopus())
      strictEqual(prevArtboards.length, 0)
    })

    it('should include newly added artboards in the current artboard list', () => {
      const design = new Design()
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const prevArtboards = design.getArtboards()

      design.addArtboard('a', createOctopus())

      const nextArtboards = design.getArtboards()
      strictEqual(nextArtboards.length, 1)
      ok(nextArtboards[0])
      strictEqual(nextArtboards[0].id, 'a')
    })

    it('should keep newly removed artboards in a previously returned artboard list', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus())

      const prevArtboards = design.getArtboards()
      design.removeArtboard('a')

      strictEqual(prevArtboards.length, 1)
      ok(prevArtboards[0])
      strictEqual(prevArtboards[0].id, 'a')
    })

    it('should not include newly removed artboards in the current artboard list', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus())

      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const prevArtboards = design.getArtboards()
      design.removeArtboard('a')

      const nextArtboards = design.getArtboards()
      strictEqual(nextArtboards.length, 0)
    })
  })

  describe('page-specific artboard lists', () => {
    it('should return empty page artboard lists by default', () => {
      const design = new Design()

      strictEqual(design.getPageArtboards('p1').length, 0)
    })

    it('should include added artboards in their respective page artboard lists', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { pageId: 'p1' })
      design.addArtboard('b', createOctopus(), { pageId: 'p2' })

      const pageArtboards1 = design.getPageArtboards('p1')
      strictEqual(pageArtboards1.length, 1)
      ok(pageArtboards1[0])
      strictEqual(pageArtboards1[0].id, 'a')

      const pageArtboards2 = design.getPageArtboards('p2')
      strictEqual(pageArtboards2.length, 1)
      ok(pageArtboards2[0])
      strictEqual(pageArtboards2[0].id, 'b')
    })

    it('should not include newly added artboards in a previously returned page artboard list', () => {
      const design = new Design()

      const prevArtboards = design.getPageArtboards('p1')

      design.addArtboard('a', createOctopus(), { pageId: 'p1' })
      strictEqual(prevArtboards.length, 0)
    })

    it('should include newly added artboards in the current artboard list', () => {
      const design = new Design()
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const prevArtboards = design.getPageArtboards('p1')

      design.addArtboard('a', createOctopus(), { pageId: 'p1' })

      const nextArtboards = design.getPageArtboards('p1')
      strictEqual(nextArtboards.length, 1)
      ok(nextArtboards[0])
      strictEqual(nextArtboards[0].id, 'a')
    })
  })

  describe('master component artboards', () => {
    it('should return an added master component artboard by component ID', () => {
      const design = new Design()

      design.addArtboard('a', { ...createOctopus(), 'symbolID': 'abc' })

      const artboard = design.getArtboardByComponentId('abc')
      ok(artboard)
      strictEqual(artboard.id, 'a')
    })

    it('should not return a removed master component artboard by component ID', () => {
      const design = new Design()

      design.addArtboard('a', { ...createOctopus(), 'symbolID': 'abc' })
      design.removeArtboard('a')

      const artboard = design.getArtboardByComponentId('abc')
      strictEqual(artboard, null)
    })
  })

  describe('master component artboard list', () => {
    it('should return an empty master component artboard list by default', () => {
      const design = new Design()

      strictEqual(design.getComponentArtboards().length, 0)
    })

    it('should include an added artboard in the master component artboard list', () => {
      const design = new Design()

      design.addArtboard('a', { ...createOctopus(), 'symbolID': 'abc' })

      const artboards = design.getComponentArtboards()
      strictEqual(artboards.length, 1)
      ok(artboards[0])
      strictEqual(artboards[0].id, 'a')
    })

    it('should not include newly added artboards in a previously returned master component artboard list', () => {
      const design = new Design()

      const prevArtboards = design.getComponentArtboards()

      design.addArtboard('a', { ...createOctopus(), 'symbolID': 'abc' })
      strictEqual(prevArtboards.length, 0)
    })

    it('should include newly added artboards in the current master component artboard list', () => {
      const design = new Design()
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const prevArtboards = design.getComponentArtboards()

      design.addArtboard('a', { ...createOctopus(), 'symbolID': 'abc' })

      const nextArtboards = design.getComponentArtboards()
      strictEqual(nextArtboards.length, 1)
      ok(nextArtboards[0])
      strictEqual(nextArtboards[0].id, 'a')
    })

    it('should keep newly removed artboards in a previously returned master component artboard list', () => {
      const design = new Design()

      design.addArtboard('a', { ...createOctopus(), 'symbolID': 'abc' })

      const prevArtboards = design.getComponentArtboards()
      design.removeArtboard('a')

      strictEqual(prevArtboards.length, 1)
      ok(prevArtboards[0])
      strictEqual(prevArtboards[0].id, 'a')
    })

    it('should not include newly removed artboards in the current master component artboard list', () => {
      const design = new Design()

      design.addArtboard('a', { ...createOctopus(), 'symbolID': 'abc' })

      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const prevArtboards = design.getComponentArtboards()
      design.removeArtboard('a')

      const nextArtboards = design.getComponentArtboards()
      strictEqual(nextArtboards.length, 0)
    })
  })

  describe('artboard info', () => {
    it('should not configure the artboard name by default', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus())

      const artboard = design.getArtboardById('a')
      ok(artboard)
      strictEqual(artboard.name, null)
    })

    it('should configure the artboard name when specified', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Hello' })

      const artboard = design.getArtboardById('a')
      ok(artboard)
      strictEqual(artboard.name, 'Hello')
    })

    it('should not configure the artboard page ID by default', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus())

      const artboard = design.getArtboardById('a')
      ok(artboard)
      strictEqual(artboard.pageId, null)
    })

    it('should configure the artboard page ID when specified', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { pageId: 'p1' })

      const artboard = design.getArtboardById('a')
      ok(artboard)
      strictEqual(artboard.pageId, 'p1')
    })
  })

  describe('single artboard lookup', () => {
    it('should look up an added artboard by exact name match', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })

      const artboard = design.findArtboard({ name: 'Abc' })
      ok(artboard)
      strictEqual(artboard.id, 'a')
    })

    it('should look up the first added artboard exactly matching one of the listed names', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.addArtboard('b', createOctopus(), { name: 'Def' })

      const artboard = design.findArtboard({ name: ['Abc', 'Def'] })
      ok(artboard)
      strictEqual(artboard.id, 'a')
    })

    it('should look up the first added artboard matching a name pattern', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.addArtboard('b', createOctopus(), { name: 'Def' })

      const artboard = design.findArtboard({ name: /Abc|Def/ })
      ok(artboard)
      strictEqual(artboard.id, 'a')
    })

    it('should not look up any of the added artboards when neither matches the name', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })

      const artboard = design.findArtboard({ name: 'Unknown' })
      strictEqual(artboard, null)
    })

    it('should not look up a removed artboard when matching by name', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.removeArtboard('a')

      const artboard = design.findArtboard({ name: 'Abc' })
      strictEqual(artboard, null)
    })
  })

  describe('multi-artboard lookup', () => {
    it('should look up added artboards by exact name match sorted by addition order', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.addArtboard('b', createOctopus(), { name: 'Def' })
      design.addArtboard('c', createOctopus(), { name: 'Abc' })

      const artboards = design.findArtboards({ name: 'Abc' })
      strictEqual(artboards.length, 2)
      ok(artboards[0])
      ok(artboards[1])
      strictEqual(artboards[0].id, 'a')
      strictEqual(artboards[1].id, 'c')
    })

    it('should look up added artboards exactly matching one of the listed names', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.addArtboard('b', createOctopus(), { name: 'Xyz' })
      design.addArtboard('c', createOctopus(), { name: 'Def' })

      const artboards = design.findArtboards({ name: ['Abc', 'Def'] })
      strictEqual(artboards.length, 2)
      ok(artboards[0])
      ok(artboards[1])
      strictEqual(artboards[0].id, 'a')
      strictEqual(artboards[1].id, 'c')
    })

    it('should look up the first added artboard matching a name pattern', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.addArtboard('b', createOctopus(), { name: 'Xyz' })
      design.addArtboard('c', createOctopus(), { name: 'Def' })

      const artboards = design.findArtboards({ name: /Abc|Def/ })
      strictEqual(artboards.length, 2)
      ok(artboards[0])
      ok(artboards[1])
      strictEqual(artboards[0].id, 'a')
      strictEqual(artboards[1].id, 'c')
    })

    it('should not look up any of the added artboards when neither matches the name', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.addArtboard('b', createOctopus(), { name: 'Def' })

      const artboards = design.findArtboards({ name: 'Unknown' })
      strictEqual(artboards.length, 0)
    })

    it('should not look up removed artboards when matching by name', () => {
      const design = new Design()

      design.addArtboard('a', createOctopus(), { name: 'Abc' })
      design.addArtboard('b', createOctopus(), { name: 'Abc' })
      design.removeArtboard('a')

      const artboards = design.findArtboards({ name: 'Abc' })
      strictEqual(artboards.length, 1)
      ok(artboards[0])
      strictEqual(artboards[0].id, 'b')
    })
  })

  describe('layer lookup defaults', () => {
    it('should not return any individual layers based on an ID by default', () => {
      const design = new Design()

      strictEqual(design.findLayerById('abc'), null)
    })

    it('should not return any layers based on an ID by default', () => {
      const design = new Design()

      strictEqual(design.findLayersById('abc').length, 0)
    })

    it('should not return any individual layers based on a selector by default', () => {
      const design = new Design()

      strictEqual(design.findLayer({ name: 'abc' }), null)
    })

    it('should not return any layers based on a selector by default', () => {
      const design = new Design()

      strictEqual(design.findLayers({ name: 'abc' }).length, 0)
    })
  })

  describe('artboard data aggregation defaults', () => {
    it('should not return any bitmap assets by default', () => {
      const design = new Design()

      strictEqual(design.getBitmapAssets().length, 0)
    })

    it('should not return any fonts by default', () => {
      const design = new Design()

      strictEqual(design.getFonts().length, 0)
    })

    it('should return an empty flattened layer list by default', () => {
      const design = new Design()

      strictEqual(design.getFlattenedLayers().length, 0)
    })
  })
})
Example #7
Source File: layer.spec.ts    From open-design-sdk with Apache License 2.0 4 votes vote down vote up
describe('Layer', () => {
  function createOctopus<T extends Partial<OctopusDocument>>(
    data: T
  ): OctopusDocument & T {
    const width = Math.round(Math.random() * 400)
    const height = Math.round(Math.random() * 400)

    return {
      'frame': {
        'x': Math.round(Math.random() * 400),
        'y': Math.round(Math.random() * 400),
      },
      'bounds': {
        'left': 0,
        'top': 0,
        'right': width,
        'bottom': height,
        'width': width,
        'height': height,
      },
      'layers': [],
      ...data,
    }
  }

  function createLayerOctopus<T extends Partial<LayerOctopusData>>(
    data: T
  ): LayerOctopusData {
    const id = String(Math.round(Math.random() * 400))
    const base = {
      'id': `layer-${id}`,
      'name': `Layer ID=${id}`,
      'type': 'layer',
    }
    const type = data['type'] || base['type']

    // @ts-expect-error Hard to generalize.
    return {
      ...base,
      ...(type === 'textLayer' ? { 'text:': {} } : {}),
      ...(type === 'groupLayer' ? { 'layers:': [] } : {}),
      ...data,
    }
  }

  describe('layer info', () => {
    it('should have the ID from octopus', () => {
      const layer = new Layer(createLayerOctopus({ 'id': 'a' }))

      strictEqual(layer.id, 'a')
    })

    it('should have the provided octopus', () => {
      const octopus = createLayerOctopus({ 'id': 'a' })
      const layer = new Layer({ ...octopus })

      deepStrictEqual(layer.octopus, octopus)
    })

    it('should have the name from octopus', () => {
      const layer = new Layer(createLayerOctopus({ 'id': 'a', 'name': 'Abc' }))

      strictEqual(layer.name, 'Abc')
    })

    it('should have the type from octopus', () => {
      const layer = new Layer(
        createLayerOctopus({ 'id': 'a', 'type': 'shapeLayer' })
      )

      strictEqual(layer.type, 'shapeLayer')
    })

    it('should return the provided artboard', () => {
      const artboard = new Artboard('x', createOctopus({}))
      const layer = new Layer(createLayerOctopus({ 'id': 'a' }), { artboard })

      strictEqual(layer.getArtboard(), artboard)
    })
  })

  describe('parent layer info', () => {
    it('should not return a parent layer ID when not parent layer ID is specified', () => {
      const layerOctopus = createLayerOctopus({ 'id': 'a' })
      const artboardOctopus = createOctopus({
        'layers': [layerOctopus],
      })

      const layer = new Layer(layerOctopus, {
        parentLayerId: null,
        artboard: new Artboard('x', artboardOctopus),
      })

      strictEqual(layer.getParentLayerId(), null)
    })

    it('should not return a parent layer ID when neither a parent layer ID is specified nor an artboard instance is provided', () => {
      const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
        parentLayerId: null,
        artboard: null,
      })

      strictEqual(layer.getParentLayerId(), null)
    })

    it('should return the specified parent layer ID', () => {
      const layerOctopus = createLayerOctopus({ 'id': 'a' })
      const artboardOctopus = createOctopus({
        'layers': [
          createLayerOctopus({
            'id': 'x',
            'layers': [
              createLayerOctopus({ 'id': 'b', 'layers': [layerOctopus] }),
            ],
          }),
        ],
      })

      const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
        parentLayerId: 'b',
        artboard: new Artboard('x', artboardOctopus),
      })

      strictEqual(layer.getParentLayerId(), 'b')
    })

    it('should return the specified parent layer ID even when an artboard instance is not provided', () => {
      const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
        parentLayerId: 'b',
        artboard: null,
      })

      strictEqual(layer.getParentLayerId(), 'b')
    })
  })

  describe('parent layers', () => {
    describe('parent layer lookup', () => {
      it('should not return a parent layer when no parent layer ID is not specified', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: null,
          artboard: new Artboard('x', artboardOctopus),
        })

        strictEqual(layer.getParentLayer(), null)
      })

      it('should not return a parent layer when neither a parent layer ID is specified nor an artboard instance is provided', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: null,
          artboard: null,
        })

        strictEqual(layer.getParentLayer(), null)
      })

      it('should fail on looking up the parent layer when a parent layer ID is specified without providing the artboard instance', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: 'b',
          artboard: null,
        })

        throws(() => {
          layer.getParentLayer()
        })
      })

      it('should fail on looking up the parent layer when the parent layer is not available on the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: 'b',
          artboard: new Artboard('x', artboardOctopus),
        })

        throws(() => {
          layer.getParentLayer()
        })
      })

      it('should return the parent layer from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'layers': [
                createLayerOctopus({ 'id': 'y', 'layers': [layerOctopus] }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'y',
          artboard,
        })

        strictEqual(layer.getParentLayer(), artboard.getLayerById('y'))
      })
    })

    describe('parent layer chain lookup', () => {
      it('should not return any parent layers when no parent layer ID is not specified', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: null,
          artboard: new Artboard('x', artboardOctopus),
        })

        strictEqual(layer.getParentLayers().length, 0)
      })

      it('should not return any parent layers when neither a parent layer ID is specified nor an artboard instance is provided', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: null,
          artboard: null,
        })

        strictEqual(layer.getParentLayers().length, 0)
      })

      it('should fail on looking up parent layers when a parent layer ID is specified without providing the artboard instance', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: 'b',
          artboard: null,
        })

        throws(() => {
          layer.getParentLayers()
        })
      })

      it('should fail on looking up parent layers when the direct parent layer is not available on the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: 'b',
          artboard: new Artboard('x', artboardOctopus),
        })

        throws(() => {
          layer.getParentLayers()
        })
      })

      it('should return the parent layer chain from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'layers': [
                createLayerOctopus({ 'id': 'y', 'layers': [layerOctopus] }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'y',
          artboard,
        })

        const parentLayers = layer.getParentLayers().getLayers()
        strictEqual(parentLayers.length, 2)
        strictEqual(parentLayers[0], artboard.getLayerById('y'))
        strictEqual(parentLayers[1], artboard.getLayerById('x'))
      })
    })

    describe('parent layer ID chain lookup', () => {
      it('should not return any parent layer IDs when no parent layer ID is not specified', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: null,
          artboard: new Artboard('x', artboardOctopus),
        })

        strictEqual(layer.getParentLayerIds().length, 0)
      })

      it('should not return any parent layer IDs when neither a parent layer ID is specified nor an artboard instance is provided', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: null,
          artboard: null,
        })

        strictEqual(layer.getParentLayerIds().length, 0)
      })

      it('should fail on looking up parent layer IDs when a parent layer ID is specified without providing the artboard instance', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: 'b',
          artboard: null,
        })

        throws(() => {
          layer.getParentLayerIds()
        })
      })

      it('should fail on looking up parent layer IDs when the direct parent layer is not available on the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: 'b',
          artboard: new Artboard('x', artboardOctopus),
        })

        throws(() => {
          layer.getParentLayerIds()
        })
      })

      it('should return the parent layer ID chain from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'layers': [
                createLayerOctopus({ 'id': 'y', 'layers': [layerOctopus] }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'y',
          artboard,
        })

        const parentLayers = layer.getParentLayerIds()
        strictEqual(parentLayers.length, 2)
        strictEqual(parentLayers[0], 'y')
        strictEqual(parentLayers[1], 'x')
      })
    })

    describe('selector-based parent layer lookup', () => {
      it('should not return a parent layer when no parent layer ID is not specified', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'x', 'name': 'Layer X' }),
            createLayerOctopus({ 'id': 'y', 'name': 'Layer Y' }),
            layerOctopus,
          ],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: null,
          artboard: new Artboard('x', artboardOctopus),
        })

        strictEqual(layer.findParentLayer({ 'name': 'Layer X' }), null)
      })

      it('should not return any parent layer when neither a parent layer ID is specified nor an artboard instance is provided', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: null,
          artboard: null,
        })

        strictEqual(layer.findParentLayer({ 'name': 'Layer X' }), null)
      })

      it('should fail on looking up parent layer when a parent layer ID is specified without providing the artboard instance', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: 'b',
          artboard: null,
        })

        throws(() => {
          layer.findParentLayer({ 'name': 'Layer X' })
        })
      })

      it('should fail on looking up parent layer when the direct parent layer is not available on the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: 'b',
          artboard: new Artboard('x', artboardOctopus),
        })

        throws(() => {
          layer.findParentLayer({ 'name': 'Layer X' })
        })
      })

      it('should return the parent layer matching the selector from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'name': 'Layer X',
              'layers': [
                createLayerOctopus({
                  'id': 'y',
                  'name': 'Layer Y',
                  'layers': [layerOctopus],
                }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'y',
          artboard,
        })

        strictEqual(
          layer.findParentLayer({ 'name': 'Layer X' }),
          artboard.getLayerById('x')
        )
      })

      it('should return the deepest parent layer matching the selector from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'name': 'ABC',
              'layers': [
                createLayerOctopus({
                  'id': 'y',
                  'name': 'ABC',
                  'layers': [layerOctopus],
                }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'y',
          artboard,
        })

        strictEqual(
          layer.findParentLayer({ name: 'ABC' }),
          artboard.getLayerById('y')
        )
      })

      it('should not return a sibling layer matching the selector from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'name': 'Layer X',
              'layers': [
                layerOctopus,
                createLayerOctopus({
                  'id': 'y',
                  'name': 'Layer Y',
                }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'x',
          artboard,
        })

        strictEqual(layer.findParentLayer({ 'name': 'Layer Y' }), null)
      })

      it('should not return a parent layer sibling layer matching the selector from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'name': 'Layer X',
              'layers': [layerOctopus],
            }),
            createLayerOctopus({
              'id': 'y',
              'name': 'Layer Y',
              'layers': [layerOctopus],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'x',
          artboard,
        })

        strictEqual(layer.findParentLayer({ 'name': 'Layer Y' }), null)
      })
    })

    describe('selector-based batch parent layer lookup', () => {
      it('should not return any parent layers when no parent layer ID is not specified', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({ 'id': 'x', 'name': 'Layer X' }),
            createLayerOctopus({ 'id': 'y', 'name': 'Layer Y' }),
            layerOctopus,
          ],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: null,
          artboard: new Artboard('x', artboardOctopus),
        })

        strictEqual(layer.findParentLayers({ 'name': 'Layer X' }).length, 0)
      })

      it('should not return any parent layer when neither a parent layer ID is specified nor an artboard instance is provided', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: null,
          artboard: null,
        })

        strictEqual(layer.findParentLayers({ 'name': 'Layer X' }).length, 0)
      })

      it('should fail on looking up parent layer when a parent layer ID is specified without providing the artboard instance', () => {
        const layer = new Layer(createLayerOctopus({ 'id': 'a' }), {
          parentLayerId: 'b',
          artboard: null,
        })

        throws(() => {
          layer.findParentLayers({ 'name': 'Layer X' })
        })
      })

      it('should fail on looking up parent layer when the direct parent layer is not available on the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          parentLayerId: 'b',
          artboard: new Artboard('x', artboardOctopus),
        })

        throws(() => {
          layer.findParentLayers({ 'name': 'Layer X' })
        })
      })

      it('should return the parent layer matching the selector from the artboard instance sorted from the deepest layer up', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'name': 'ABC',
              'layers': [
                createLayerOctopus({
                  'id': 'y',
                  'name': 'XYZ',
                  'layers': [
                    createLayerOctopus({
                      'id': 'z',
                      'name': 'ABC',
                      'layers': [layerOctopus],
                    }),
                  ],
                }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'z',
          artboard,
        })

        const parentLayers = layer.findParentLayers({ name: 'ABC' }).getLayers()
        strictEqual(parentLayers.length, 2)
        strictEqual(parentLayers[0], artboard.getLayerById('z'))
        strictEqual(parentLayers[1], artboard.getLayerById('x'))
      })

      it('should not return sibling layers matching the selector from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'name': 'Layer X',
              'layers': [
                layerOctopus,
                createLayerOctopus({
                  'id': 'y',
                  'name': 'Layer Y',
                }),
              ],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'x',
          artboard,
        })

        strictEqual(layer.findParentLayers({ 'name': 'Layer Y' }).length, 0)
      })

      it('should not return parent layer sibling layers matching the selector from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({ 'id': 'a' })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'x',
              'name': 'Layer X',
              'layers': [layerOctopus],
            }),
            createLayerOctopus({
              'id': 'y',
              'name': 'Layer Y',
              'layers': [layerOctopus],
            }),
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          parentLayerId: 'x',
          artboard,
        })

        strictEqual(layer.findParentLayers({ 'name': 'Layer Y' }).length, 0)
      })
    })
  })

  describe('nested layers', () => {
    describe('nested layer list', () => {
      it('should return the first-level nested layers', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({ 'id': 'b' }),
            createLayerOctopus({ 'id': 'c' }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer.getNestedLayers().getLayers()
        strictEqual(nestedLayers.length, 2)
        ok(nestedLayers[0])
        ok(nestedLayers[1])
        strictEqual(nestedLayers[0].id, 'b')
        strictEqual(nestedLayers[1].id, 'c')
      })

      it('should return the same first-level nested layer list on multiple calls', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({ 'id': 'b' }),
            createLayerOctopus({ 'id': 'c' }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers1 = layer.getNestedLayers()
        const nestedLayers2 = layer.getNestedLayers()
        strictEqual(nestedLayers1, nestedLayers2)
      })

      it('should not return deeper-level nested layers by default', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'layers': [createLayerOctopus({ 'id': 'c' })],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer.getNestedLayers().getLayers()
        strictEqual(nestedLayers.length, 1)
        ok(nestedLayers[0])
        strictEqual(nestedLayers[0].id, 'b')
      })

      it('should return the specified number of levels of nested layers', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'layers': [
                createLayerOctopus({
                  'id': 'c',
                  'layers': [createLayerOctopus({ 'id': 'd' })],
                }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer.getNestedLayers({ depth: 2 }).getLayers()
        strictEqual(nestedLayers.length, 2)
        ok(nestedLayers[0])
        ok(nestedLayers[1])
        strictEqual(nestedLayers[0].id, 'b')
        strictEqual(nestedLayers[1].id, 'c')
      })

      it('should return the same multi-level nested layer list on multiple calls', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'layers': [createLayerOctopus({ 'id': 'c' })],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers1 = layer.getNestedLayers({ depth: 2 })
        const nestedLayers2 = layer.getNestedLayers({ depth: 2 })
        strictEqual(nestedLayers1, nestedLayers2)
      })
    })

    describe('nested layer lookup', () => {
      it('should look up a first-level nested layer by a selector', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({ 'id': 'b', 'name': 'Layer B' }),
            createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
            createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
          ],
        })

        const layer = new Layer(layerOctopus)

        strictEqual(layer.findNestedLayer({ name: 'Layer C' })?.id, 'c')
      })

      it('should look up a deeper-level nested layer by a selector', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'layers': [
                createLayerOctopus({ 'id': 'c', 'name': 'Layer C' }),
                createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                createLayerOctopus({ 'id': 'e', 'name': 'Layer E' }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        strictEqual(layer.findNestedLayer({ name: 'Layer C' })?.id, 'c')
      })

      it('should return a matching nested layer from any levels by default', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'name': 'Layer A',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'layers': [
                createLayerOctopus({
                  'id': 'c',
                  'name': 'Layer C',
                  'layers': [
                    createLayerOctopus({ 'id': 'd', 'name': 'Layer D' }),
                  ],
                }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        strictEqual(layer.findNestedLayer({ name: 'Layer D' })?.id, 'd')
      })

      it('should return a matching highest-level nested layer', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'name': 'Layer A',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'ABC',
              'layers': [
                createLayerOctopus({
                  'id': 'c',
                  'name': 'ABC',
                  'layers': [createLayerOctopus({ 'id': 'd', 'name': 'ABC' })],
                }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        strictEqual(layer.findNestedLayer({ name: 'ABC' })?.id, 'b')
      })

      it('should return a matching deeper-level nested layer listed in a layer which precedes a matching sibling layer', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'name': 'Layer A',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'layers': [
                createLayerOctopus({
                  'id': 'c',
                  'name': 'ABC',
                }),
              ],
            }),
            createLayerOctopus({ 'id': 'd', 'name': 'ABC' }),
          ],
        })

        const layer = new Layer(layerOctopus)

        strictEqual(layer.findNestedLayer({ name: 'ABC' })?.id, 'c')
      })

      it('should not return matching nested layers from levels deeper than the specified depth', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'name': 'Layer A',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'layers': [
                createLayerOctopus({
                  'id': 'c',
                  'name': 'Layer C',
                  'layers': [createLayerOctopus({ 'id': 'd', 'name': 'ABC' })],
                }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        strictEqual(layer.findNestedLayer({ name: 'ABC' }, { depth: 2 }), null)
      })
    })

    describe('batch nested layer lookup', () => {
      it('should look up first-level nested layers by a selector', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({ 'id': 'b', 'name': 'ABC' }),
            createLayerOctopus({ 'id': 'c', 'name': 'ABC' }),
            createLayerOctopus({ 'id': 'd', 'name': 'XYZ' }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer.findNestedLayers({ name: 'ABC' }).getLayers()
        strictEqual(nestedLayers.length, 2)
        ok(nestedLayers[0])
        ok(nestedLayers[1])
        strictEqual(nestedLayers[0].id, 'b')
        strictEqual(nestedLayers[1].id, 'c')
      })

      it('should look up deeper-level nested layers by a selector', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'Layer B',
              'layers': [
                createLayerOctopus({ 'id': 'c', 'name': 'ABC' }),
                createLayerOctopus({ 'id': 'd', 'name': 'ABC' }),
                createLayerOctopus({ 'id': 'e', 'name': 'Layer E' }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer.findNestedLayers({ name: 'ABC' }).getLayers()
        strictEqual(nestedLayers.length, 2)
        ok(nestedLayers[0])
        ok(nestedLayers[1])
        strictEqual(nestedLayers[0].id, 'c')
        strictEqual(nestedLayers[1].id, 'd')
      })

      it('should return matching nested layers from all levels by default', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({ 'id': 'b1', 'name': 'ABC' }),
            createLayerOctopus({
              'id': 'b2',
              'layers': [
                createLayerOctopus({ 'id': 'c1', 'name': 'ABC' }),
                createLayerOctopus({
                  'id': 'c2',
                  'layers': [createLayerOctopus({ 'id': 'd1', 'name': 'ABC' })],
                }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer.findNestedLayers({ name: 'ABC' }).getLayers()
        strictEqual(nestedLayers.length, 3)
        ok(nestedLayers[0])
        ok(nestedLayers[1])
        ok(nestedLayers[2])
        strictEqual(nestedLayers[0].id, 'b1')
        strictEqual(nestedLayers[1].id, 'c1')
        strictEqual(nestedLayers[2].id, 'd1')
      })

      it('should not return matching nested layers containing matching deeper-level nested layers multiple times', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'ABC',
              'layers': [
                createLayerOctopus({
                  'id': 'c',
                  'name': 'ABC',
                  'layers': [createLayerOctopus({ 'id': 'd', 'name': 'ABC' })],
                }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer.findNestedLayers({ name: 'ABC' }).getLayers()
        strictEqual(nestedLayers.length, 3)
        ok(nestedLayers[0])
        ok(nestedLayers[1])
        ok(nestedLayers[2])
        strictEqual(nestedLayers[0].id, 'b')
        strictEqual(nestedLayers[1].id, 'c')
        strictEqual(nestedLayers[2].id, 'd')
      })

      it('should not return matching nested layers from levels deeper than the specified depth', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'layers': [
            createLayerOctopus({
              'id': 'b',
              'name': 'ABC',
              'layers': [
                createLayerOctopus({
                  'id': 'c',
                  'name': 'ABC',
                  'layers': [createLayerOctopus({ 'id': 'd', 'name': 'ABC' })],
                }),
              ],
            }),
          ],
        })

        const layer = new Layer(layerOctopus)

        const nestedLayers = layer
          .findNestedLayers({ name: 'ABC' }, { depth: 2 })
          .getLayers()
        strictEqual(nestedLayers.length, 2)
        ok(nestedLayers[0])
        ok(nestedLayers[1])
        strictEqual(nestedLayers[0].id, 'b')
        strictEqual(nestedLayers[1].id, 'c')
      })
    })
  })

  describe('bitmap assets', () => {
    it('should return the main bitmap of a bitmap layer as a bitmap asset', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'layer',
          'bitmap': {
            'filename': 'a.png',
            'bounds': {
              'left': 10,
              'top': 20,
              'right': 110,
              'bottom': 220,
              'width': 100,
              'height': 200,
            },
          },
        })
      )

      const bitmapAssetDescs = layer.getBitmapAssets()
      strictEqual(bitmapAssetDescs.length, 1)
      deepStrictEqual(bitmapAssetDescs[0], {
        layerIds: ['a'],
        name: 'a.png',
        prerendered: false,
      })
    })

    it('should return the prerendered bitmap of a non-bitmap layer as a prerendered bitmap asset', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'shapeLayer',
          'bitmap': {
            'filename': 'a.png',
            'bounds': {
              'left': 10,
              'top': 20,
              'right': 110,
              'bottom': 220,
              'width': 100,
              'height': 200,
            },
          },
        })
      )

      const bitmapAssetDescs = layer.getBitmapAssets()
      strictEqual(bitmapAssetDescs.length, 1)
      deepStrictEqual(bitmapAssetDescs[0], {
        layerIds: ['a'],
        name: 'a.png',
        prerendered: true,
      })
    })

    it('should return pattern fill bitmaps as bitmap assets', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'shapeLayer',
          'effects': {
            'fills': [
              {
                'pattern': {
                  'filename': 'a.png',
                  'offset': { 'horizontal': 0, 'vertical': 0 },
                  'relativeTo': 'layer',
                  'type': 'fit',
                },
              },
              {
                'pattern': {
                  'filename': 'b.png',
                  'offset': { 'horizontal': 0, 'vertical': 0 },
                  'relativeTo': 'layer',
                  'type': 'fit',
                },
              },
            ],
          },
        })
      )

      const bitmapAssetDescs = layer.getBitmapAssets()
      strictEqual(bitmapAssetDescs.length, 2)
      deepStrictEqual(bitmapAssetDescs[0], {
        layerIds: ['a'],
        name: 'a.png',
        prerendered: false,
      })
      deepStrictEqual(bitmapAssetDescs[1], {
        layerIds: ['a'],
        name: 'b.png',
        prerendered: false,
      })
    })

    it('should return pattern border bitmaps as bitmap assets', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'shapeLayer',
          'effects': {
            'borders': [
              {
                'width': 2,
                'pattern': {
                  'filename': 'a.png',
                  'offset': { 'horizontal': 0, 'vertical': 0 },
                  'relativeTo': 'layer',
                  'type': 'fit',
                },
              },
              {
                'width': 2,
                'pattern': {
                  'filename': 'b.png',
                  'offset': { 'horizontal': 0, 'vertical': 0 },
                  'relativeTo': 'layer',
                  'type': 'fit',
                },
              },
            ],
          },
        })
      )

      const bitmapAssetDescs = layer.getBitmapAssets()
      strictEqual(bitmapAssetDescs.length, 2)
      deepStrictEqual(bitmapAssetDescs[0], {
        layerIds: ['a'],
        name: 'a.png',
        prerendered: false,
      })
      deepStrictEqual(bitmapAssetDescs[1], {
        layerIds: ['a'],
        name: 'b.png',
        prerendered: false,
      })
    })

    describe('nested layers', () => {
      it('should return the main bitmaps of nested bitmap layers as bitmap assets', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'type': 'layer',
                'bitmap': {
                  'filename': 'a.png',
                  'bounds': {
                    'left': 10,
                    'top': 20,
                    'right': 110,
                    'bottom': 220,
                    'width': 100,
                    'height': 200,
                  },
                },
              }),
              createLayerOctopus({
                'id': 'c',
                'type': 'layer',
                'bitmap': {
                  'filename': 'b.png',
                  'bounds': {
                    'left': 10,
                    'top': 20,
                    'right': 110,
                    'bottom': 220,
                    'width': 100,
                    'height': 200,
                  },
                },
              }),
            ],
          })
        )

        const bitmapAssetDescs = layer.getBitmapAssets()
        strictEqual(bitmapAssetDescs.length, 2)
        deepStrictEqual(bitmapAssetDescs[0], {
          layerIds: ['b'],
          name: 'a.png',
          prerendered: false,
        })
        deepStrictEqual(bitmapAssetDescs[1], {
          layerIds: ['c'],
          name: 'b.png',
          prerendered: false,
        })
      })

      it('should return the main bitmaps of nested non-bitmap layers as bitmap assets', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'type': 'shapeLayer',
                'bitmap': {
                  'filename': 'a.png',
                  'bounds': {
                    'left': 10,
                    'top': 20,
                    'right': 110,
                    'bottom': 220,
                    'width': 100,
                    'height': 200,
                  },
                },
              }),
              createLayerOctopus({
                'id': 'c',
                'type': 'shapeLayer',
                'bitmap': {
                  'filename': 'b.png',
                  'bounds': {
                    'left': 10,
                    'top': 20,
                    'right': 110,
                    'bottom': 220,
                    'width': 100,
                    'height': 200,
                  },
                },
              }),
            ],
          })
        )

        const bitmapAssetDescs = layer.getBitmapAssets()
        strictEqual(bitmapAssetDescs.length, 2)
        deepStrictEqual(bitmapAssetDescs[0], {
          layerIds: ['b'],
          name: 'a.png',
          prerendered: true,
        })
        deepStrictEqual(bitmapAssetDescs[1], {
          layerIds: ['c'],
          name: 'b.png',
          prerendered: true,
        })
      })

      it('should return pattern fill bitmaps in nested layers as bitmap assets', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'type': 'shapeLayer',
                'effects': {
                  'fills': [
                    {
                      'pattern': {
                        'filename': 'a.png',
                        'offset': { 'horizontal': 0, 'vertical': 0 },
                        'relativeTo': 'layer',
                        'type': 'fit',
                      },
                    },
                    {
                      'pattern': {
                        'filename': 'b.png',
                        'offset': { 'horizontal': 0, 'vertical': 0 },
                        'relativeTo': 'layer',
                        'type': 'fit',
                      },
                    },
                  ],
                },
              }),
              createLayerOctopus({
                'id': 'c',
                'name': 'C',
                'type': 'shapeLayer',
                'effects': {
                  'fills': [
                    {
                      'pattern': {
                        'filename': 'c.png',
                        'offset': { 'horizontal': 0, 'vertical': 0 },
                        'relativeTo': 'layer',
                        'type': 'fit',
                      },
                    },
                  ],
                },
              }),
            ],
          })
        )

        const bitmapAssetDescs = layer.getBitmapAssets()
        strictEqual(bitmapAssetDescs.length, 3)
        deepStrictEqual(bitmapAssetDescs[0], {
          layerIds: ['b'],
          name: 'a.png',
          prerendered: false,
        })
        deepStrictEqual(bitmapAssetDescs[1], {
          layerIds: ['b'],
          name: 'b.png',
          prerendered: false,
        })
        deepStrictEqual(bitmapAssetDescs[2], {
          layerIds: ['c'],
          name: 'c.png',
          prerendered: false,
        })
      })

      it('should return pattern border bitmaps in nested layers as bitmap assets', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'type': 'shapeLayer',
                'effects': {
                  'borders': [
                    {
                      'width': 2,
                      'pattern': {
                        'filename': 'a.png',
                        'offset': { 'horizontal': 0, 'vertical': 0 },
                        'relativeTo': 'layer',
                        'type': 'fit',
                      },
                    },
                    {
                      'width': 2,
                      'pattern': {
                        'filename': 'b.png',
                        'offset': { 'horizontal': 0, 'vertical': 0 },
                        'relativeTo': 'layer',
                        'type': 'fit',
                      },
                    },
                  ],
                },
              }),
              createLayerOctopus({
                'id': 'c',
                'type': 'shapeLayer',
                'effects': {
                  'borders': [
                    {
                      'width': 2,
                      'pattern': {
                        'filename': 'c.png',
                        'offset': { 'horizontal': 0, 'vertical': 0 },
                        'relativeTo': 'layer',
                        'type': 'fit',
                      },
                    },
                  ],
                },
              }),
            ],
          })
        )

        const bitmapAssetDescs = layer.getBitmapAssets()
        strictEqual(bitmapAssetDescs.length, 3)
        deepStrictEqual(bitmapAssetDescs[0], {
          layerIds: ['b'],
          name: 'a.png',
          prerendered: false,
        })
        deepStrictEqual(bitmapAssetDescs[1], {
          layerIds: ['b'],
          name: 'b.png',
          prerendered: false,
        })
        deepStrictEqual(bitmapAssetDescs[2], {
          layerIds: ['c'],
          name: 'c.png',
          prerendered: false,
        })
      })

      it('should not return bitmap assets from nested layers at levels deeper than the specified depth', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'type': 'layer',
                'bitmap': {
                  'filename': 'a.png',
                  'bounds': {
                    'left': 10,
                    'top': 20,
                    'right': 110,
                    'bottom': 220,
                    'width': 100,
                    'height': 200,
                  },
                },
              }),
              createLayerOctopus({
                'id': 'c',
                'type': 'groupLayer',
                'layers': [
                  {
                    'id': 'd',
                    'name': 'D',
                    'type': 'layer',
                    'bitmap': {
                      'filename': 'b.png',
                      'bounds': {
                        'left': 10,
                        'top': 20,
                        'right': 110,
                        'bottom': 220,
                        'width': 100,
                        'height': 200,
                      },
                    },
                  },
                ],
              }),
            ],
          })
        )

        const bitmapAssetDescs = layer.getBitmapAssets({ depth: 2 })
        strictEqual(bitmapAssetDescs.length, 1)
        deepStrictEqual(bitmapAssetDescs[0], {
          layerIds: ['b'],
          name: 'a.png',
          prerendered: false,
        })
      })
    })
  })

  describe('fonts', () => {
    it('should return the default text style font', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'layer',
          'text': {
            'value': 'Oh my text',
            'defaultStyle': {
              'font': {
                'name': 'Abc',
                'type': 'Black',
                'postScriptName': 'PostAbc',
                'syntheticPostScriptName': false,
              },
            },
          },
        })
      )

      const fontDescs = layer.getFonts()
      strictEqual(fontDescs.length, 1)
      deepStrictEqual(fontDescs[0], {
        layerIds: ['a'],
        fontPostScriptName: 'PostAbc',
        fontPostScriptNameSynthetic: false,
        fontTypes: ['Black'],
      })
    })

    it('should flag synthetic default text style font postscript names', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'layer',
          'text': {
            'value': 'Oh my text',
            'defaultStyle': {
              'font': {
                'name': 'Abc',
                'type': 'Black',
                'postScriptName': 'PostAbc',
                'syntheticPostScriptName': true,
              },
            },
          },
        })
      )

      const fontDescs = layer.getFonts()
      strictEqual(fontDescs.length, 1)
      strictEqual(fontDescs[0]?.fontPostScriptNameSynthetic, true)
    })

    it('should return text range style fonts', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'textLayer',
          'text': {
            'value': 'Oh my text',
            'styles': [
              {
                'ranges': [{ 'from': 0, 'to': 10 }],
                'font': {
                  'name': 'Abc',
                  'type': 'Black',
                  'postScriptName': 'PostAbc',
                  'size': 10,
                },
              },
            ],
          },
        })
      )

      const fontDescs = layer.getFonts()
      strictEqual(fontDescs.length, 1)
      deepStrictEqual(fontDescs[0], {
        layerIds: ['a'],
        fontPostScriptName: 'PostAbc',
        fontPostScriptNameSynthetic: false,
        fontTypes: ['Black'],
      })
    })

    it('should flag synthetic text range style font postscript names', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'textLayer',
          'text': {
            'value': 'Oh my text',
            'styles': [
              {
                'ranges': [{ 'from': 0, 'to': 10 }],
                'font': {
                  'name': 'Abc',
                  'type': 'Black',
                  'postScriptName': 'PostAbc',
                  'syntheticPostScriptName': true,
                  'size': 10,
                },
              },
            ],
          },
        })
      )

      const fontDescs = layer.getFonts()
      strictEqual(fontDescs.length, 1)
      strictEqual(fontDescs[0]?.fontPostScriptNameSynthetic, true)
    })

    it('should merge multiple types of the same font into a single item', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'textLayer',
          'text': {
            'value': 'Oh my text',
            'styles': [
              {
                'ranges': [{ 'from': 0, 'to': 10 }],
                'font': {
                  'name': 'Abc',
                  'type': 'Black',
                  'postScriptName': 'PostAbc',
                  'size': 10,
                },
              },
              {
                'ranges': [{ 'from': 10, 'to': 20 }],
                'font': {
                  'name': 'Abc',
                  'type': 'Normal',
                  'postScriptName': 'PostAbc',
                  'size': 10,
                },
              },
            ],
          },
        })
      )

      const fontDescs = layer.getFonts()
      strictEqual(fontDescs.length, 1)
      deepStrictEqual(fontDescs[0], {
        layerIds: ['a'],
        fontPostScriptName: 'PostAbc',
        fontPostScriptNameSynthetic: false,
        fontTypes: ['Black', 'Normal'],
      })
    })

    describe('nested layers', () => {
      it('should return the default text style font from nested layers', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'type': 'textLayer',
                'text': {
                  'value': 'Oh my text',
                  'defaultStyle': {
                    'font': {
                      'name': 'Abc',
                      'type': 'Black',
                      'postScriptName': 'PostAbc',
                      'syntheticPostScriptName': false,
                      'size': 10,
                    },
                  },
                },
              }),
            ],
          })
        )

        const fontDescs = layer.getFonts()
        strictEqual(fontDescs.length, 1)
        deepStrictEqual(fontDescs[0], {
          layerIds: ['b'],
          fontPostScriptName: 'PostAbc',
          fontPostScriptNameSynthetic: false,
          fontTypes: ['Black'],
        })
      })

      it('should return text range style fonts from nested layers', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'type': 'groupLayer',
            'layers': [
              createLayerOctopus({
                'id': 'b',
                'name': 'B',
                'type': 'textLayer',
                'text': {
                  'value': 'Oh my text',
                  'styles': [
                    {
                      'ranges': [{ 'from': 0, 'to': 10 }],
                      'font': {
                        'name': 'Abc',
                        'type': 'Black',
                        'postScriptName': 'PostAbc',
                        'size': 10,
                      },
                    },
                  ],
                },
              }),
            ],
          })
        )

        const fontDescs = layer.getFonts()
        strictEqual(fontDescs.length, 1)
        deepStrictEqual(fontDescs[0], {
          layerIds: ['b'],
          fontPostScriptName: 'PostAbc',
          fontPostScriptNameSynthetic: false,
          fontTypes: ['Black'],
        })
      })
    })
  })

  describe('masks', () => {
    it('should claim it is masked when the clipped flag is set', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'layer',
          'clipped': true,
          'maskedBy': 'b',
        })
      )

      strictEqual(layer.isMasked(), true)
    })

    it('should claim it is not masked when the clipped flag is not set', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'layer',
          'clipped': false,
          'maskedBy': undefined,
        })
      )

      strictEqual(layer.isMasked(), false)
    })

    it('should return the mask layer from octopus', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'layer',
          'clipped': true,
          'maskedBy': 'b',
        })
      )

      strictEqual(layer.getMaskLayerId(), 'b')
    })

    it('should not return a mask layer ID when not set in octopus', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'type': 'layer',
          'clipped': false,
          'maskedBy': undefined,
        })
      )

      strictEqual(layer.getMaskLayerId(), null)
    })

    describe('mask layers', () => {
      it('should not return a mask layer when a mask layer is not set in octopus', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'clipped': false,
          'maskedBy': undefined,
        })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          artboard: new Artboard('x', artboardOctopus),
        })

        strictEqual(layer.getMaskLayer(), null)
      })

      it('should not return a mask layer when neither a mask layer is set in octopus nor an artboard instance is provided', () => {
        const layer = new Layer(
          createLayerOctopus({
            'id': 'a',
            'clipped': false,
            'maskedBy': undefined,
          }),
          {
            artboard: null,
          }
        )

        strictEqual(layer.getMaskLayer(), null)
      })

      it('should fail on looking up the mask layer when a mask layer ID is set in octopus without providing the artboard instance', () => {
        const layer = new Layer(
          createLayerOctopus({ 'id': 'a', 'clipped': true, 'maskedBy': 'b' }),
          {
            artboard: null,
          }
        )

        throws(() => {
          layer.getMaskLayer()
        })
      })

      it('should fail on looking up the mask layer when the mask layer is not available on the artboard instance', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'clipped': true,
          'maskedBy': 'b',
        })
        const artboardOctopus = createOctopus({
          'layers': [layerOctopus],
        })

        const layer = new Layer(layerOctopus, {
          artboard: new Artboard('x', artboardOctopus),
        })

        throws(() => {
          layer.getMaskLayer()
        })
      })

      it('should return the mask layer from the artboard instance', () => {
        const layerOctopus = createLayerOctopus({
          'id': 'a',
          'clipped': true,
          'maskedBy': 'b',
        })
        const artboardOctopus = createOctopus({
          'layers': [
            createLayerOctopus({
              'id': 'b',
            }),
            layerOctopus,
          ],
        })

        const artboard = new Artboard('x', artboardOctopus)
        const layer = new Layer(layerOctopus, {
          artboard,
        })

        strictEqual(layer.getMaskLayer(), artboard.getLayerById('b'))
      })
    })
  })

  describe('components', () => {
    it('should claim it is a component instance when it has a symbol ID set in octopus', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'symbolID': 'x',
        })
      )

      strictEqual(layer.isComponentInstance(), true)
    })

    it('should claim it is a component instance when it has a document ID set in octopus', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'documentId': 'y',
        })
      )

      strictEqual(layer.isComponentInstance(), true)
    })

    it('should claim it is not a component instance when it has neither a symbol ID nor a document ID set in octopus', () => {
      const layer = new Layer(
        createLayerOctopus({
          'id': 'a',
          'symbolID': undefined,
          'documentId': undefined,
        })
      )

      strictEqual(layer.isComponentInstance(), false)
    })
  })
})
Example #8
Source File: sdk.spec.ts    From open-design-sdk with Apache License 2.0 4 votes vote down vote up
describe('DesignFacade', () => {
  describe('local files', () => {
    it('should return the manifest read from disk', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const { octopusFilename, manifest } = await createOctopusFile(
        'file.octopus'
      )
      const designFacade = await sdk.openOctopusFile(octopusFilename)

      deepStrictEqual(designFacade.getManifest(), manifest)
    })

    it('should return artboard octopus data read from disk', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const { octopusFilename, artboardOctopuses } = await createOctopusFile(
        'file.octopus'
      )
      const designFacade = await sdk.openOctopusFile(octopusFilename)

      const artboardFacade = designFacade.getArtboardById('a')
      ok(artboardFacade)
      deepStrictEqual(await artboardFacade.getContent(), artboardOctopuses['a'])
    })

    it('should save its manifest to a new location', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const {
        octopusFilename: originalFilename,
        manifest,
      } = await createOctopusFile('original.octopus')
      const designFacade = await sdk.openOctopusFile(originalFilename)

      const copyFilename = await createTempFileTarget('copy.octopus')
      await designFacade.saveOctopusFile({ filePath: copyFilename })

      deepStrictEqual(
        JSON.parse(readFileSync(`${copyFilename}/manifest.json`, 'utf8')),
        manifest
      )
    })

    it('should save its artboard octopus data to a new location', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const {
        octopusFilename: originalFilename,
        artboardOctopuses,
      } = await createOctopusFile('original.octopus')
      const designFacade = await sdk.openOctopusFile(originalFilename)

      const copyFilename = await createTempFileTarget('copy.octopus')
      await designFacade.saveOctopusFile({ filePath: copyFilename })

      deepStrictEqual(
        JSON.parse(
          readFileSync(`${copyFilename}/artboards/a/data.json`, 'utf8')
        ),
        artboardOctopuses['a']
      )
    })

    it('should save its bitmaps referenced via relative paths in octopus data to a new location', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const {
        octopusFilename: originalFilename,
        bitmapFilenames,
        bitmapMapping,
      } = await createOctopusFile('original.octopus')

      const designFacade = await sdk.openOctopusFile(originalFilename)

      const copyFilename = await createTempFileTarget('copy.octopus')
      await designFacade.saveOctopusFile({ filePath: copyFilename })

      deepStrictEqual(
        JSON.parse(readFileSync(`${copyFilename}/bitmaps.json`, 'utf8')),
        bitmapMapping
      )

      Object.entries(bitmapFilenames).forEach(
        ([_bitmapKey, bitmapFilename]) => {
          statSync(bitmapFilename)
        }
      )
    })

    it('should fail opening a non-existent octopus file', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const randomFilename = await createTempFileTarget('random-file.octopus')
      const [result] = await Promise.allSettled([
        sdk.openOctopusFile(randomFilename),
      ])
      strictEqual(result.status, 'rejected')
    })

    it('should fail opening an octopus file without the .octopus extension', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const { octopusFilename } = await createOctopusFile('file.random')
      const [result] = await Promise.allSettled([
        sdk.openOctopusFile(octopusFilename),
      ])
      strictEqual(result.status, 'rejected')
    })

    it('should fail opening an octopus file without a manifest', async () => {
      const { sdk } = await createSdk({ localDesigns: true })

      const { octopusFilename } = await createOctopusFile('file.octopus')
      unlinkSync(`${octopusFilename}/manifest.json`)

      const [result] = await Promise.allSettled([
        sdk.openOctopusFile(octopusFilename),
      ])
      strictEqual(result.status, 'rejected')
    })
  })

  describe('design uploads', () => {
    it('should return contents of an uploaded design', async function () {
      const { sdk } = await createSdk({ designFiles: true, api: true })

      const designFacade = await sdk.importDesignFile(
        singleArtboardSketchFileFixture.filename
      )

      const [fixtureArtboardDesc] = singleArtboardSketchFileFixture.artboards
      ok(fixtureArtboardDesc)

      const artboardFacade = designFacade.findArtboard({
        name: fixtureArtboardDesc.name,
      })
      ok(artboardFacade)

      const octopus = await artboardFacade.getContent()
      strictEqual(octopus['bounds']?.['width'], fixtureArtboardDesc.width)
      strictEqual(octopus['bounds']?.['height'], fixtureArtboardDesc.height)
    })

    it('should list all bitmaps in an uploaded design', async function () {
      const { sdk } = await createSdk({ designFiles: true, api: true })

      const designFacade = await sdk.importDesignFile(
        multiArtboardSketchFileFixture.filename
      )

      const bitmapAssetDesc = await designFacade.getBitmapAssets()
      strictEqual(
        bitmapAssetDesc.length,
        multiArtboardSketchFileFixture.bitmapCount
      )
    })
  })

  describe('design downloading', () => {
    let designId: string
    let tokenFromBefore: string

    before(async function () {
      const { sdk, token } = await createSdk({
        designFiles: true,
        api: true,
      })
      ok(token)
      tokenFromBefore = token

      const designFacade = await sdk.importDesignFile(
        singleArtboardSketchFileFixture.filename
      )
      ok(designFacade.id)
      designId = designFacade.id
    })

    it('should return contents of an uploaded design', async function () {
      const { sdk } = await createSdk({ token: tokenFromBefore, api: true })
      const designFacade = await sdk.fetchDesignById(designId)

      const [fixtureArtboardDesc] = singleArtboardSketchFileFixture.artboards
      ok(fixtureArtboardDesc)

      const artboardFacade = designFacade.findArtboard({
        name: fixtureArtboardDesc.name,
      })
      ok(artboardFacade)

      const octopus = await artboardFacade.getContent()
      strictEqual(octopus['bounds']?.['width'], fixtureArtboardDesc.width)
      strictEqual(octopus['bounds']?.['height'], fixtureArtboardDesc.height)
    })

    it('should save contents of an uploaded design', async function () {
      const { sdk } = await createSdk({
        token: tokenFromBefore,
        localDesigns: true,
        api: true,
      })

      const designFacade = await sdk.fetchDesignById(designId)

      const filename = await createTempFileTarget('file.octopus')
      await designFacade.saveOctopusFile({ filePath: filename })

      const manifest: ManifestData = JSON.parse(
        readFileSync(`${filename}/manifest.json`, 'utf8')
      )
      strictEqual(
        manifest['artboards'].length,
        singleArtboardSketchFileFixture.artboards.length
      )

      const [fixtureArtboardDesc] = singleArtboardSketchFileFixture.artboards
      ok(fixtureArtboardDesc)

      const octopus: OctopusDocument = JSON.parse(
        readFileSync(
          `${filename}/artboards/${fixtureArtboardDesc.id}/data.json`,
          'utf8'
        )
      )
      strictEqual(octopus['bounds']?.['width'], fixtureArtboardDesc.width)
      strictEqual(octopus['bounds']?.['height'], fixtureArtboardDesc.height)
    })

    it('should save info about the API design', async function () {
      const { sdk, apiRoot } = await createSdk({
        token: tokenFromBefore,
        localDesigns: true,
        api: true,
      })

      const designFacade = await sdk.fetchDesignById(designId)

      const filename = await createTempFileTarget('file.octopus')
      await designFacade.saveOctopusFile({ filePath: filename })

      const apiDesignInfo: ApiDesignInfo = JSON.parse(
        readFileSync(`${filename}/api-design.json`, 'utf8')
      )
      deepStrictEqual(apiDesignInfo, { apiRoot, designId })
    })

    it('should cache the manifest of a fetched design in a local file', async function () {
      const { sdk } = await createSdk({
        token: tokenFromBefore,
        localDesigns: true,
        api: true,
      })

      const designFacade = await sdk.fetchDesignById(designId)

      const filename = designFacade.octopusFilename
      ok(filename)

      const manifest: ManifestData = JSON.parse(
        readFileSync(`${filename}/manifest.json`, 'utf8')
      )
      strictEqual(
        manifest['artboards'].length,
        singleArtboardSketchFileFixture.artboards.length
      )
    })

    it('should cache artboard octopuses of a fetched design in a local file', async function () {
      const { sdk } = await createSdk({
        token: tokenFromBefore,
        localDesigns: true,
        api: true,
      })

      const designFacade = await sdk.fetchDesignById(designId)

      const filename = designFacade.octopusFilename
      ok(filename)

      const [fixtureArtboardDesc] = singleArtboardSketchFileFixture.artboards
      ok(fixtureArtboardDesc)

      const artboardFacade = designFacade.getArtboardById(
        fixtureArtboardDesc.id
      )
      const artboardOctopus = await artboardFacade?.getContent()
      ok(artboardOctopus)

      const localOctopus: OctopusDocument = JSON.parse(
        readFileSync(
          `${filename}/artboards/${fixtureArtboardDesc.id}/data.json`,
          'utf8'
        )
      )
      deepStrictEqual(localOctopus, artboardOctopus)
    })

    it('should continue using the API design reference after reopening the cache', async function () {
      const { sdk } = await createSdk({
        token: tokenFromBefore,
        localDesigns: true,
        api: true,
      })

      const designFacade = await sdk.fetchDesignById(designId)
      const filename = designFacade.octopusFilename
      ok(filename)

      const reopenedDesignFacade = await sdk.openOctopusFile(filename)

      const [fixtureArtboardDesc] = singleArtboardSketchFileFixture.artboards
      ok(fixtureArtboardDesc)

      const artboardFacade = reopenedDesignFacade.getArtboardById(
        fixtureArtboardDesc.id
      )
      const artboardOctopus = await artboardFacade?.getContent()
      ok(artboardOctopus)
    })
  })

  describe('asset downloading', () => {
    it('should download all bitmaps from an uploaded design', async function () {
      const { sdk } = await createSdk({
        designFiles: true,
        localDesigns: true,
        api: true,
      })

      const designFacade = await sdk.importDesignFile(
        multiArtboardSketchFileFixture.filename
      )

      const filename = designFacade.octopusFilename
      ok(filename)

      const bitmapAssetDescs = await designFacade.getBitmapAssets()
      await designFacade.downloadBitmapAssets(bitmapAssetDescs)

      doesNotThrow(() => {
        const bitmapBasenames = readdirSync(`${filename}/bitmaps`)
        strictEqual(
          bitmapBasenames.length,
          multiArtboardSketchFileFixture.bitmapCount
        )
      })
    })

    it('should download prerendered bitmaps from an uploaded design to a subdirectory', async function () {
      const { sdk } = await createSdk({
        designFiles: true,
        localDesigns: true,
        api: true,
      })

      const designFacade = await sdk.importDesignFile(
        singleInlineArtboardPhotoshopFileFixture.filename
      )

      const filename = designFacade.octopusFilename
      ok(filename)

      const bitmapAssetDescs = await designFacade.getBitmapAssets()
      await designFacade.downloadBitmapAssets(bitmapAssetDescs)

      doesNotThrow(() => {
        const bitmapBasenames = readdirSync(`${filename}/bitmaps/prerendered`)
        strictEqual(
          bitmapBasenames.length,
          singleInlineArtboardPhotoshopFileFixture.prerenderedBitmapCount
        )
      })
    })
  })

  describe('figma designs', () => {
    let designId: string
    let tokenFromBefore: string

    before(async function () {
      const { sdk, token } = await createSdk({ api: true })
      ok(token)
      tokenFromBefore = token

      const designFacade = await sdk.importFigmaDesign({
        figmaToken: process.env['E2E_FIGMA_TOKEN'] || null,
        figmaFileKey: process.env['E2E_FIGMA_FILE_KEY'] || '',
      })
      ok(designFacade.id)
      designId = designFacade.id
    })

    it('should save a Sketch export result file', async function () {
      const { sdk } = await createSdk({
        token: tokenFromBefore,
        designFiles: true,
        api: true,
      })

      const designFacade = await sdk.fetchDesignById(designId)

      const filename = await createTempFileTarget('file.sketch')
      await designFacade.exportDesignFile(filename)

      doesNotThrow(() => {
        const stats = statSync(filename)
        ok(stats.isFile())
        ok(stats.size > 0)
      })
    })
  })

  describe('figma designs with immediate conversion', () => {
    it('should save a Sketch conversion result file', async function () {
      const { sdk } = await createSdk({ designFiles: true, api: true })

      const designFacade = await sdk.convertFigmaDesign({
        figmaToken: process.env['E2E_FIGMA_TOKEN'] || null,
        figmaFileKey: process.env['E2E_FIGMA_FILE_KEY'] || '',
        exports: [{ format: 'sketch' }],
      })

      const filename = await createTempFileTarget('file.sketch')
      await designFacade.exportDesignFile(filename)

      doesNotThrow(() => {
        const stats = statSync(filename)
        ok(stats.isFile())
        ok(stats.size > 0)
      })
    })
  })
})