fs-extra#writeFile TypeScript Examples

The following examples show how to use fs-extra#writeFile. 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: Exporter.ts    From joplin-utils with MIT License 6 votes vote down vote up
async writeResource(list: ExportResource[]) {
    await mkdirp(path.resolve(this.config.rootPath, 'resources'))
    await AsyncArray.forEach(list, async (item) => {
      await writeFile(
        path.resolve(this.config.rootPath, 'resources', item.fileTitle),
        await resourceApi.fileByResourceId(item.id),
      )
    })
  }
Example #2
Source File: Application.ts    From joplin-utils with MIT License 6 votes vote down vote up
writeNote(noteList: (CommonNote & { text: string })[]) {
    return PromiseUtil.warpOnEvent(async (events: ProcessEvents) => {
      let i = 0
      await AsyncArray.forEach(noteList, async (item) => {
        i++
        events.process({ rate: i, all: noteList.length, title: item.title })
        await writeFile(
          path.resolve(this.handler.notePath, (this.handler.formatFileName ?? ((id) => id))(item.id) + '.md'),
          item.text,
        )
      })
    })
  }
Example #3
Source File: Application.ts    From joplin-utils with MIT License 6 votes vote down vote up
copyResources(resourceList: CommonResource[]) {
    return PromiseUtil.warpOnEvent(async (events: ProcessEvents) => {
      let i = 0
      await AsyncArray.forEach(
        resourceList,
        asyncLimiting(async (resource: CommonResource) => {
          i++
          events.process({
            rate: i,
            all: resourceList.length,
            title: resource.title,
          })
          const fileName = resource.id + '.' + resource.file_extension
          await writeFile(
            path.resolve(this.handler.resourcePath, fileName),
            await resourceApi.fileByResourceId(resource.id),
          )
        }, 10),
      )
    })
  }
Example #4
Source File: BinCommand.ts    From joplin-blog with MIT License 6 votes vote down vote up
/**
   * 生成类型定义
   */
  async gen(options: { input?: string }) {
    if (!options.input) {
      return
    }
    const i18nFolderPath = path.resolve(options.input)
    const list = await readdir(i18nFolderPath)
    const i18nJsonPathList = list
      .filter((name) => name.endsWith('.json'))
      .map((name) => path.resolve(i18nFolderPath, name))
    const jsonList = await AsyncArray.map(i18nJsonPathList, (filePath) =>
      readJSON(filePath),
    )
    const translateHandler = new TranslateHandler()
    const configList = translateHandler.parse(jsonList)
    const dts = translateHandler.build(configList)
    await writeFile(path.resolve(i18nFolderPath, 'index.d.ts'), dts)
  }
Example #5
Source File: Sticker.ts    From wa-sticker-formatter with MIT License 6 votes vote down vote up
/**
     * Saves the sticker to a file
     * @param [filename] - Filename to save the sticker to
     * @returns filename
     * @example
     * const sticker = new Sticker('./image.png')
     * sticker.toFile('./image.webp')
     */
    public toFile = async (filename = this.defaultFilename): Promise<string> => {
        await writeFile(filename, await this.build())
        return filename
    }
Example #6
Source File: write-properties-file.ts    From relate with GNU General Public License v3.0 6 votes vote down vote up
export function writePropertiesFile(path: string, properties: PropertyEntries): Promise<void> {
    const asText = join(
        map(properties, ([key, val]) => {
            const nowhitespace = trim(val);

            return nowhitespace ? join([key, val], PROPERTIES_SEPARATOR) : key;
        }),
        NEW_LINE,
    );

    return writeFile(path, asText, 'utf8');
}
Example #7
Source File: Exporter.ts    From joplin-utils with MIT License 6 votes vote down vote up
async writeNote(list: ExportNote[]) {
    await AsyncArray.forEach(list, async (item) => {
      const notePath = path.resolve(
        this.config.rootPath,
        'notes',
        item.filePath,
      )
      await writeFile(notePath, item.body)
    })
  }
Example #8
Source File: main.ts    From DefinitelyTyped-tools with MIT License 6 votes vote down vote up
export async function withFileLock(lockFilePath: string, cb: () => Promise<void>): Promise<LockFileResult> {
  console.log("Checking for lock file...");
  if (await pathExists(lockFilePath)) {
    const lastRunStartTimestamp = (await tryReadJson(lockFilePath, isLockfileFormat))?.timestamp || currentTimeStamp();
    const elapsedSeconds = (Date.now() - Date.parse(lastRunStartTimestamp)) / 1000;
    if (elapsedSeconds < getFunctionTimeoutSeconds()) {
      console.log("Lock file exists; new run not triggered.");
      return { triggered: false, timestamp: lastRunStartTimestamp };
    }
  }

  console.log("Lock file does not exist; writing lock file and running.");
  await writeFile(lockFilePath, JSON.stringify({ timestamp: currentTimeStamp() }));
  cb().then(
    () => remove(lockFilePath),
    async (error) => {
      console.error(error?.stack || error?.message || error);
      applicationinsights.defaultClient.trackException({
        exception: error,
      });

      await remove(lockFilePath);
      process.exit(1);
    }
  );

  return { triggered: true };
}
Example #9
Source File: dump.ts    From cli with Apache License 2.0 6 votes vote down vote up
private async writeChunks(allResults: SearchResult[]) {
    const {flags} = await this.parse(Dump);
    let currentChunk = 0;
    while (allResults.length) {
      const chunk = allResults.splice(0, flags.chunkSize);
      const data = chunk.map((r) => r.raw);
      const fields = without(
        Object.keys(chunk[0].raw),
        flags.fieldsToExclude || []
      );
      const parser = new Parser({fields});
      await writeFile(
        `${flags.destination}/${flags.name}${
          currentChunk > 0 ? `_${currentChunk + 1}` : ''
        }.csv`,
        parser.parse(data)
      );
      currentChunk++;
    }
  }
Example #10
Source File: install-manager.ts    From malagu with MIT License 6 votes vote down vote up
async render(): Promise<void> {
        const pkg = require('../../package.json');
        const packageJsonPath = resolve(this.outputDir, 'package.json');
        let packageContent = await readFile(packageJsonPath, { encoding: 'utf8' });
        packageContent = packageContent.replace(/{{ version }}/g, pkg.version);
        if (this.opts.forceInstallingComponent) {
            const runtimePkg = JSON.parse(packageContent);
            if (runtimePkg.malagu?.components || runtimePkg.malagu?.devComponents) {
                const { components, devComponents } = runtimePkg.malagu;
                runtimePkg.dependencies = { ...runtimePkg.dependencies, ...components };
                runtimePkg.devDependencies = { ...runtimePkg.devDependencies, ...devComponents };
                packageContent = JSON.stringify(runtimePkg, undefined, 2);
            }
        }
        await writeFile(packageJsonPath, packageContent);
    }
Example #11
Source File: HandlerService.ts    From joplin-utils with MIT License 6 votes vote down vote up
async openResource(id: string) {
    const resource = await safePromise(resourceApi.get(id, ['id', 'title', 'filename', 'file_extension']))
    if (!resource) {
      vscode.window.showWarningMessage(i18n.t('Resource does not exist'))
      return
    }
    if (GlobalContext.openResourceMap.has(id)) {
      const filePath = GlobalContext.openResourceMap.get(id)!
      await this.openFileService.openByVSCode(filePath)
      return
    }
    // 如果标题包含后缀则不再拼接后缀名(后缀名其实也是不准的)
    const fileName =
      resource.filename ||
      (/\..*$/.test(resource.title) ? resource.title : resource.title + '.' + resource.file_extension)
    const tempResourceDirPath = path.resolve(GlobalContext.context.globalStorageUri.fsPath, '.tempResource')
    const filePath = path.resolve(tempResourceDirPath, filenamify(fileName))
    const buffer = await resourceApi.fileByResourceId(id)
    await writeFile(filePath, buffer)
    GlobalContext.openResourceMap.set(id, filePath)
    console.log('open file: ', filePath)
    await this.openFileService.openByVSCode(filePath)
  }
Example #12
Source File: JoplinNoteCommandService.ts    From joplin-utils with MIT License 6 votes vote down vote up
/**
   * open note in vscode
   * @param item
   */
  async openNote(item: Omit<FolderOrNote, 'item'> & { item: JoplinListNote }) {
    logger.info('openNote start', item)
    // 如果已经打开了笔记,则应该切换并且保持 treeview 的焦点
    if (GlobalContext.openNoteMap.has(item.id)) {
      const filePath = GlobalContext.openNoteMap.get(item.id)!
      if (vscode.window.activeTextEditor?.document.fileName !== filePath) {
        await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(filePath))
      }
      await this.focus(item.id)
      return
    }
    const tempNoteDirPath = path.resolve(GlobalContext.context.globalStorageUri.fsPath, '.tempNote')
    const filename = item.label + (GlobalContext.openNoteMap.get(item.label) ? item.id : '')
    const tempNotePath = path.resolve(tempNoteDirPath, filenamify(`${filename}.md`))
    const note = await noteApi.get(item.id, ['body', 'title'])
    const content = note.body.startsWith('# ')
      ? note.body
      : (note.title.startsWith('# ') ? '' : '# ') + note.title + '\n\n' + note.body
    await writeFile(tempNotePath, content)
    logger.info('openNote write tempFile', tempNotePath)
    GlobalContext.openNoteMap.set(item.id, tempNotePath)
    GlobalContext.openNoteResourceMap.set(item.id, await noteApi.resourcesById(item.id))
    await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(tempNotePath))
    logger.info('openNote open tempFile')
  }
Example #13
Source File: download-ui5-resources.ts    From ui5-language-assistant with Apache License 2.0 6 votes vote down vote up
async function writeUrlToFile(url: string, file: string): Promise<void> {
  // Don't download the file if it already exists
  if (await pathExists(file)) {
    return;
  }

  log(`fetching from ${url}`);
  const response = await fetch(url);
  if (!response.ok) {
    error(`error fetching from ${url}`);
    return;
  }
  const text = await response.text();
  if (text === "{}") {
    // These files don't add anything to the model but they return an error in strict mode
    log(`empty object returned from ${url}`);
    return;
  }
  await writeFile(file, text);
}
Example #14
Source File: deleteDuplicateNote.test.ts    From joplin-utils with MIT License 6 votes vote down vote up
describe('删除重复的笔记', () => {
  const tempPath = path.resolve(__dirname, '.temp/exportDuplicationNoteList')
  beforeEach(async () => {
    await remove(tempPath)
    await mkdirp(tempPath)
  })
  it('加载所有重复的笔记', async () => {
    const noteList = await PageUtil.pageToAllList(noteApi.list, {
      fields: ['id', 'title', 'user_updated_time', 'body'],
    })
    const map = groupBy(noteList, (note) => note.title)
    const deleteNoteList = Object.entries(map)
      .filter(([_, notes]) => notes.length > 1)
      .map(
        ([_, notes]) =>
          sortBy(
            notes as Pick<
              NoteProperties,
              'user_updated_time' | 'title' | 'body' | 'id'
            >[],
            (note) => note.user_updated_time,
          )[0],
      )
    await mkdirp(tempPath)
    for (let note of deleteNoteList) {
      const filePath = path.resolve(tempPath, note.title.trim() + '.md')
      try {
        await writeFile(filePath, note.body, {
          encoding: 'utf-8',
        })
        await noteApi.remove(note.id)
      } catch (e) {
        console.log('删除失败: ', note.title)
      }
    }
  })
})
Example #15
Source File: AttachmentsFilesService.ts    From node-experience with MIT License 6 votes vote down vote up
static async  getTempFilesAttachments(emailNotification: EmailNotification): Promise<IFilesAttachments[]>
    {
        const filesystem = FilesystemFactory.create();

        emailNotification.tempFilesAttachments =  await Promise.all(emailNotification.attachedFiles.map(async(_file) =>
        {
            const stream = await filesystem.downloadStreamFile(_file);
            const fileName = `${_file.originalName}.${_file.extension}`;
            const uqFileName = `${_file.name}.${_file.extension}`;
            const tempDir = PATH.join(`${__dirname}/../../../temp`);
            const dirName = PATH.join(`${tempDir}/${uqFileName}`);
            // eslint-disable-next-line no-unused-expressions
            !existsSync(tempDir) && mkdirSync(tempDir);
            void await writeFile(dirName, '01011');
            const ws = createWriteStream(dirName);
            stream.pipe(ws);

            return {
                filename: fileName,
                path:dirName
            };
        }));

        return emailNotification.tempFilesAttachments;
    }
Example #16
Source File: videoToGif.ts    From wa-sticker-formatter with MIT License 6 votes vote down vote up
videoToGif = async (data: Buffer): Promise<Buffer> => {
    const filename = `${tmpdir()}/${Math.random().toString(36)}`
    const [video, gif] = ['video', 'gif'].map((ext) => `${filename}.${ext}`)
    await writeFile(video, data)
    await new Promise((resolve) => {
        ffmpeg(video).save(gif).on('end', resolve)
    })
    const buffer = await readFile(gif)
    ;[video, gif].forEach((file) => unlink(file))
    return buffer
}
Example #17
Source File: ResourceWriter.ts    From joplin-blog with MIT License 5 votes vote down vote up
async write(note: CommonNote & { text: string }) {
    await writeFile(
      path.resolve(this.config.postPath, note.id + '.md'),
      note.text,
    )
  }
Example #18
Source File: WikiDocsifyIntegrated.ts    From joplin-utils with MIT License 5 votes vote down vote up
async init() {
    await writeFile(path.resolve(this.config.rootPath, '_sidebar.md'), await this.buildSidebar())
  }
Example #19
Source File: Application.test.ts    From joplin-utils with MIT License 5 votes vote down vote up
describe('测试 Application', () => {
  it.skip('单独测试 HexoIntegrated', async () => {
    const hexoHandler = new BlogHexoIntegrated({
      tag: 'blog',
      rootPath: path.resolve(__dirname, '.temp/hexo-example'),
    })
    const noteId = '21a3eba7f4b445ccbc123bf52831d387'
    const { user_created_time, user_updated_time, ...note } = await noteApi.get(noteId, [
      'id',
      'title',
      'body',
      'user_created_time',
      'user_updated_time',
    ])
    const tags = await noteApi.tagsById(noteId)
    const resources = await noteApi.resourcesById(noteId, ['id', 'title', 'file_extension', 'user_updated_time'])
    const res = hexoHandler.parse({
      ...note,
      createdTime: user_created_time,
      updatedTime: user_updated_time,
      tags,
      resources,
    })
    await writeFile(path.resolve(__dirname, '.temp/test.md'), res)
  })

  it('集成 HexoIntegrated', async () => {
    const application = new Application(
      { token: config.token, baseUrl: config.baseUrl, tag: 'blog' },
      new BlogHexoIntegrated({
        tag: 'blog',
        rootPath: path.resolve(__dirname, '.temp/hexo-example'),
      }),
    )

    const generatorEvents = new GeneratorEventsImpl()
    await application
      .gen()
      .on('readNoteAttachmentsAndTags', generatorEvents.readNoteAttachmentsAndTags)
      .on('parseAndWriteNotes', generatorEvents.parseAndWriteNotes)
      .on('writeNote', generatorEvents.writeNote)
      .on('copyResources', generatorEvents.copyResources)
  }, 100_000)
  it('集成 VuepressIntegrated', async () => {
    const application = new Application(
      {
        token: config.token,
        baseUrl: config.baseUrl,
        tag: 'blog',
      },
      new BlogVuepressIntegrated({
        rootPath: path.resolve(__dirname, '.temp/vuepress-example'),
        tag: 'blog',
      }),
    )

    const generatorEvents = new GeneratorEventsImpl()
    await application
      .gen()
      .on('readNoteAttachmentsAndTags', generatorEvents.readNoteAttachmentsAndTags)
      .on('parseAndWriteNotes', generatorEvents.parseAndWriteNotes)
      .on('writeNote', generatorEvents.writeNote)
      .on('copyResources', generatorEvents.copyResources)
  }, 100_000)
})
Example #20
Source File: wikiWorker.ts    From TidGi-Desktop with Mozilla Public License 2.0 5 votes vote down vote up
function executeZxScript(file: IZxFileInput, zxPath: string): Observable<IZxWorkerMessage> {
  /** this will be observed in src/services/native/index.ts */
  return new Observable<IZxWorkerMessage>((observer) => {
    observer.next({ type: 'control', actions: ZxWorkerControlActions.start });

    void (async function executeZxScriptIIFE() {
      try {
        let filePathToExecute = '';
        if ('fileName' in file) {
          const temporaryDirectory = await mkdtemp(`${tmpdir()}${path.sep}`);
          filePathToExecute = path.join(temporaryDirectory, file.fileName);
          await writeFile(filePathToExecute, file.fileContent);
        } else if ('filePath' in file) {
          filePathToExecute = file.filePath;
        }
        const execution = fork(zxPath, [filePathToExecute], { silent: true });

        execution.on('close', function (code) {
          observer.next({ type: 'control', actions: ZxWorkerControlActions.ended, message: `child process exited with code ${String(code)}` });
        });
        execution.stdout?.on('data', (stdout: Buffer) => {
          // if there are multiple console.log, their output will be concatenated into this stdout. And some of them are not intended to be executed. We use TW_SCRIPT_SEPARATOR to allow user determine the range they want to execute in the $tw context.
          const message = String(stdout);
          const zxConsoleLogMessages = extractTWContextScripts(message);
          // log and execute each different console.log result.
          zxConsoleLogMessages.forEach(({ messageType, content }) => {
            if (messageType === 'script') {
              observer.next({ type: 'execution', message: content });
              if (wikiInstance === undefined) {
                observer.next({ type: 'stderr', message: `Error in executeZxScript(): $tw is undefined` });
              } else {
                const context = getTWVmContext(wikiInstance);
                const twExecutionResult = executeScriptInTWContext(content, context);
                observer.next({ type: 'stdout', message: twExecutionResult.join('\n\n') });
              }
            } else {
              observer.next({ type: 'stdout', message: content });
            }
          });
        });
        execution.stderr?.on('data', (stdout: Buffer) => {
          observer.next({ type: 'stderr', message: String(stdout) });
        });
        execution.on('error', (error) => {
          observer.next({ type: 'stderr', message: `${error.message} ${error.stack ?? ''}` });
        });
      } catch (error) {
        const message = `zx script's executeZxScriptIIFE() failed with error ${(error as Error).message} ${(error as Error).stack ?? ''}`;
        observer.next({ type: 'control', actions: ZxWorkerControlActions.error, message });
      }
    })();
  });
}
Example #21
Source File: WikiDocsifyIntegrated.ts    From joplin-blog with MIT License 5 votes vote down vote up
async init() {
    await this.resourceWriter.clean()
    await writeFile(
      path.resolve(this.config.rootPath, '_sidebar.md'),
      await this.buildSidebar(),
    )
  }
Example #22
Source File: postInstall.ts    From cli with MIT License 5 votes vote down vote up
postInstallModule = async (
  moduleList: { name: string; version: string }[]
) => {
  const info = await postInstall();
  if (!info) {
    return;
  }
  const { cwd, pkg } = info;
  const { registry, npm } = findNpm();
  const modules = [];
  for (const { name, version } of moduleList) {
    if (pkg?.dependencies?.[name] || pkg?.devDependencies?.[name]) {
      continue;
    }
    console.log('[midway] auto install', name);
    modules.push(name + '@' + version);
  }

  if (!modules.length) {
    return;
  }
  const installingLock = join(
    cwd,
    `node_modules/.midwayjs-cli/postInstallLock/${modules
      .join('_')
      .replace(/\//g, '_')}.lock`
  );
  if (existsSync(installingLock)) {
    return;
  }
  await ensureFile(installingLock);
  await writeFile(installingLock, JSON.stringify({ cwd, npm, registry }));
  await installNpm({
    baseDir: cwd,
    mode: ['save-dev'],
    register: ['yarn'].includes(npm) ? 'npm' : npm,
    registerPath: registry,
    moduleName: modules.join(' '),
    slience: true,
  });
  console.log('[midway] auto install complete');
  return;
}
Example #23
Source File: yarn.ts    From malagu with MIT License 5 votes vote down vote up
writeLockfile(lockfilePath: string, content: string) {
        return writeFile(lockfilePath, content, 'utf8');
    }
Example #24
Source File: convert.ts    From wa-sticker-formatter with MIT License 5 votes vote down vote up
convert = async (
    data: Buffer,
    mime: string,
    { quality = 100, background = defaultBg, type = StickerTypes.DEFAULT }: IStickerOptions
): Promise<Buffer> => {
    const isVideo = mime.startsWith('video')
    let image = isVideo ? await videoToGif(data) : data
    const isAnimated = isVideo || mime.includes('gif')

    if (isAnimated && ['crop', 'circle'].includes(type)) {
        const filename = `${tmpdir()}/${Math.random().toString(36)}.webp`
        await writeFile(filename, image)
        ;[image, type] = [await crop(filename), type === 'circle' ? StickerTypes.CIRCLE : StickerTypes.DEFAULT]
    }

    const img = sharp(image, { animated: type !== 'circle' }).toFormat('webp')

    if (type === 'crop')
        img.resize(512, 512, {
            fit: fit.cover
        })

    if (type === 'full')
        img.resize(512, 512, {
            fit: fit.contain,
            background
        })

    if (type === 'circle') {
        img.resize(512, 512, {
            fit: fit.cover
        }).composite([
            {
                input: Buffer.from(
                    `<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><circle cx="256" cy="256" r="256" fill="${background}"/></svg>`
                ),
                blend: 'dest-in'
            }
        ])
    }

    return await img
        .webp({
            quality,
            lossless: false
        })
        .toBuffer()
}
Example #25
Source File: Application.test.ts    From joplin-blog with MIT License 4 votes vote down vote up
describe('测试 Application', () => {
  const joplinConfig: typeof config = {
    token: process.env.token!,
    port: Number.parseInt(process.env.port!),
  }

  it('单独测试 HexoIntegrated', async () => {
    config.token = joplinConfig.token
    config.port = joplinConfig.port
    const hexoHandler = new BlogHexoIntegrated({
      tag: 'blog',
      rootPath: path.resolve(__dirname, 'temp/hexo-example'),
    })
    const noteId = '21a3eba7f4b445ccbc123bf52831d387'
    const {
      user_created_time,
      user_updated_time,
      ...note
    } = await noteApi.get(noteId, [
      'id',
      'title',
      'body',
      'user_created_time',
      'user_updated_time',
    ])
    const tags = await noteApi.tagsById(noteId)
    const resources = await noteApi.resourcesById(noteId, [
      'id',
      'title',
      'file_extension',
    ])
    const res = hexoHandler.parse({
      ...note,
      createdTime: user_created_time,
      updatedTime: user_updated_time,
      tags,
      resources,
    })
    await writeFile(path.resolve(__dirname, 'temp/test.md'), res)
  })

  it('集成 HexoIntegrated', async () => {
    const application = new Application(
      {
        token: joplinConfig.token,
        port: joplinConfig.port,
        tag: 'blog',
        joplinProfilePath: path.resolve(
          'C:/Users/rxliuli/.config/joplindev-desktop',
        ),
      },
      new BlogHexoIntegrated({
        tag: 'blog',
        rootPath: path.resolve(__dirname, 'temp/hexo-example'),
      }),
    )

    const generatorEvents = new GeneratorEventsImpl()
    await application
      .gen()
      .on(
        'readNoteAttachmentsAndTags',
        generatorEvents.readNoteAttachmentsAndTags,
      )
      .on('parseAndWriteNotes', generatorEvents.parseAndWriteNotes)
      .on('writeNote', generatorEvents.writeNote)
      .on('copyResources', generatorEvents.copyResources)
  }, 100_000)
  it('集成 VuepressIntegrated', async () => {
    const application = new Application(
      {
        token: joplinConfig.token,
        port: joplinConfig.port,
        tag: 'blog',
        joplinProfilePath: path.resolve(
          'C:/Users/rxliuli/.config/joplindev-desktop',
        ),
      },
      new BlogVuepressIntegrated({
        rootPath: path.resolve(__dirname, 'temp/vuepress-example'),
        tag: 'blog',
      }),
    )

    const generatorEvents = new GeneratorEventsImpl()
    await application
      .gen()
      .on(
        'readNoteAttachmentsAndTags',
        generatorEvents.readNoteAttachmentsAndTags,
      )
      .on('parseAndWriteNotes', generatorEvents.parseAndWriteNotes)
      .on('writeNote', generatorEvents.writeNote)
      .on('copyResources', generatorEvents.copyResources)
  }, 100_000)
})
Example #26
Source File: WindowDecorations.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN a text document with decorations", function () {
  const CREATED = "1625648278263";
  const UPDATED = "1625758878263";
  const FNAME = "bar";

  describe("AND GIVEN links ", () => {
    function checkTimestampsDecorated({
      decorations,
      document,
    }: {
      decorations: Awaited<ReturnType<typeof updateDecorations>>;
      document: vscode.TextDocument;
    }) {
      const { allDecorations } = decorations;
      const timestampDecorations = getDecorations({
        allDecorations,
        decorationType: EDITOR_DECORATION_TYPES.timestamp,
      });
      expect(timestampDecorations!.length).toEqual(2);
      // check that the decorations are at the right locations
      checkDecoration({
        text: CREATED,
        decorations: timestampDecorations,
        document,
      });
      checkDecoration({
        text: UPDATED,
        decorations: timestampDecorations,
        document,
      });
    }

    describeMultiWS(
      "",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          await NoteTestUtilsV4.createNote({
            fname: "withHeader",
            vault: vaults[0],
            wsRoot,
            body: "## ipsam adipisci",
          });
          await NoteTestUtilsV4.createNote({
            fname: "tags.bar",
            vault: vaults[0],
            wsRoot,
          });
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: [
              "Ut incidunt id commodi. ^anchor-1",
              "",
              "* Et repudiandae optio ut suscipit illum hic vel.",
              "* Aut suscipit veniam nobis veniam reiciendis. ^anchor-2",
              "  * Sit sed accusamus saepe voluptatem sint animi quis animi. ^anchor-3",
              "* Dolores suscipit maiores nulla accusamus est.",
              "",
              "#foo",
              "#bar",
              "#foo",
              "[[root]]",
              "",
              "@Hamilton.Margaret",
              "",
              "[[with alias|root]]",
              "",
              "![[root.*#head]]",
              "",
              "[[withHeader#ipsam-adipisci]]",
              "[[withHeader#does-not-exist]]",
              "",
              "[[does.not.exist]]",
              "",
              "[[/test.txt]]",
              "[[/test.txt#L3]]",
            ].join("\n"),
            props: {
              created: _.toInteger(CREATED),
              updated: _.toInteger(UPDATED),
              tags: ["foo", "bar"],
            },
            vault: vaults[0],
            wsRoot,
          });
          await writeFile(
            path.join(wsRoot, "test.txt"),
            "et\nnam\nvelit\nlaboriosam\n"
          );
        },
      },
      () => {
        // TODO: this is currently a regression from refactoring engine
        test.skip("THEN links are decorated", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const decorations = (await updateDecorations(editor))!;
          const { allDecorations } = decorations;

          checkTimestampsDecorated({ decorations, document });

          const blockAnchorDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.blockAnchor
          );
          expect(blockAnchorDecorations!.length).toEqual(3);
          // check that the decorations are at the right locations
          expect(
            isTextDecorated("^anchor-1", blockAnchorDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("^anchor-2", blockAnchorDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("^anchor-3", blockAnchorDecorations!, document)
          ).toBeTruthy();

          const wikilinkDecorations = getDecorations({
            allDecorations,
            decorationType: EDITOR_DECORATION_TYPES.wikiLink,
          });
          expect(wikilinkDecorations!.length).toEqual(8);
          expect(
            isTextDecorated("[[root]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "[[with alias|root]]",
              wikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated("#bar", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("![[root.*#head]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "[[withHeader#ipsam-adipisci]]",
              wikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated("[[/test.txt]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("[[/test.txt#L3]]", wikilinkDecorations!, document)
          ).toBeTruthy();

          const aliasDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.alias
          );
          expect(aliasDecorations!.length).toEqual(1);
          expect(
            isTextDecorated("with alias", aliasDecorations!, document)
          ).toBeTruthy();

          const brokenWikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.brokenWikilink
          );
          expect(brokenWikilinkDecorations!.length).toEqual(5);
          expect(
            isTextDecorated(
              "[[does.not.exist]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "[[withHeader#does-not-exist]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "@Hamilton.Margaret",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated("#foo", brokenWikilinkDecorations!, document)
          ).toBeTruthy();
          return;
        });
      }
    );
  });

  describe("AND GIVEN task notes", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: "with.all",
            vault: vaults[0],
            wsRoot,
            custom: {
              status: "done",
              owner: "grace",
              priority: "H",
              due: "2021.10.29",
              tags: "foo",
            },
          });
          await NoteTestUtilsV4.createNote({
            fname: "without.status",
            vault: vaults[0],
            wsRoot,
            custom: {
              owner: "grace",
              priority: "high",
              tags: ["foo", "bar"],
            },
          });
          await NoteTestUtilsV4.createNote({
            fname: "without.due",
            vault: vaults[0],
            wsRoot,
            custom: {
              status: "",
              priority: "low",
            },
          });
          await NoteTestUtilsV4.createNote({
            fname: "not.a.task",
            vault: vaults[0],
            wsRoot,
          });
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: [
              "* [[with.all]]",
              "* foo [[without.status]] bar",
              "",
              "[[without.due]]",
              "",
              "[[not.a.task]]",
              "",
            ].join("\n"),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN task notes are highlighted", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const { allDecorations } = (await updateDecorations(editor))!;

          const taskDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.taskNote
          );
          taskDecorations?.sort((decoration) => decoration.range.start.line); // for easier testing
          expect(taskDecorations!.length).toEqual(3);
          // check that the decorations are at the right locations
          expect(
            isTextDecorated("[[with.all]]", taskDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("[[without.status]]", taskDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("[[without.due]]", taskDecorations!, document)
          ).toBeTruthy();

          expect(
            taskDecorations![0].renderOptions?.before?.contentText
          ).toEqual("[ ] ");
          expect(taskDecorations![0].renderOptions?.after?.contentText).toEqual(
            " priority:low"
          );
          expect(
            taskDecorations![1].renderOptions?.before?.contentText
          ).toEqual("[x] ");
          expect(taskDecorations![1].renderOptions?.after?.contentText).toEqual(
            " due:2021.10.29 @grace priority:high #foo"
          );
          expect(
            taskDecorations![2].renderOptions?.before?.contentText
          ).toBeFalsy();
          expect(taskDecorations![2].renderOptions?.after?.contentText).toEqual(
            " @grace priority:high #foo #bar"
          );
        });
      }
    );
  });

  describe("AND GIVEN file with wikilinks to itself", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: [
              "Ut incidunt id commodi. ^anchor-1",
              "",
              "[[#^anchor-1]]",
              "[[#^anchor-not-exists]]",
              "![[#^anchor-1]]",
              "![[#^anchor-not-exists]]",
              "![[#^anchor-1:#*]]",
              "![[#^anchor-not-exists]]",
            ].join("\n"),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN links are highlighted", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const { allDecorations } = (await updateDecorations(editor))!;

          const wikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.wikiLink
          );
          expect(wikilinkDecorations!.length).toEqual(3);
          expect(
            isTextDecorated("[[#^anchor-1]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("![[#^anchor-1]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "![[#^anchor-1:#*]]",
              wikilinkDecorations!,
              document
            )
          ).toBeTruthy();

          const brokenWikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.brokenWikilink
          );
          expect(brokenWikilinkDecorations!.length).toEqual(3);
          expect(
            isTextDecorated(
              "[[#^anchor-not-exists]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "![[#^anchor-not-exists]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "![[#^anchor-not-exists]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
        });
      }
    );
  });

  describe("AND given wildcard references", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: ["![[foo.bar.*]]"].join("\n"),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN links are highlighted", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const { allDecorations } = (await updateDecorations(editor))!;

          const wikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.wikiLink
          );
          expect(wikilinkDecorations!.length).toEqual(1);
          expect(
            isTextDecorated("![[foo.bar.*]]", wikilinkDecorations!, document)
          ).toBeTruthy();
        });
      }
    );
  });

  describe("AND for long notes", () => {
    const FNAME = "test.note";
    const repeat = 228;
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: _.repeat("[[does.not.exist]] #does.not.exist\n", repeat),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN only the visible range should be decorated", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;

          const { allDecorations } = (await updateDecorations(editor))!;

          // This note is really long, so not all links in it will be decorated (there are repeat * 2 many links)
          const brokenWikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.brokenWikilink
          );
          expect(brokenWikilinkDecorations!.length < repeat * 2).toBeTruthy();
          expect(
            isTextDecorated(
              "[[does.not.exist]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "#does.not.exist",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
        });
      }
    );
  });

  describe("AND WHEN disabled", () => {
    describeMultiWS(
      "",
      {
        modConfigCb: (config) => {
          config.workspace!.enableEditorDecorations = false;
          return config;
        },
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: "[[does.not.exist]] #does.not.exist\n",
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN decorations are not displayed ", async () => {
          const { editor } = await getNote({ fname: FNAME });

          const { allDecorations, allWarnings } = (await updateDecorations(
            editor
          ))!;

          expect(allDecorations).toBeFalsy();
          expect(allWarnings).toBeFalsy();
        });
      }
    );
  });

  describe("AND GIVEN warnings in document", () => {
    describeMultiWS(
      "AND WHEN missing frontmatter",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          const note = await NoteTestUtilsV4.createNote({
            fname: FNAME,
            vault: vaults[0],
            wsRoot,
          });
          // Empty out the note, getting rid of the frontmatter
          const path = NoteUtils.getFullPath({ note, wsRoot });
          await writeFile(path, "foo bar");
        },
      },
      () => {
        test("THEN show frontmatter missing warning", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const { allWarnings } = (await updateDecorations(editor))!;

          expect(allWarnings!.length).toEqual(1);
          expect(
            AssertUtils.assertInString({
              body: allWarnings![0].message,
              match: ["frontmatter", "missing"],
            })
          );
        });
      }
    );

    describeMultiWS(
      "AND WHEN bad note id",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            vault: vaults[0],
            wsRoot,
            props: {
              id: "-foo",
            },
          });
        },
      },
      () => {
        test("THEN show frontmatter missing warning", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const { allWarnings } = (await updateDecorations(editor))!;
          expect(allWarnings!.length).toEqual(1);
          expect(
            AssertUtils.assertInString({
              body: allWarnings![0].message,
              match: ["id", "bad"],
            })
          );
        });
      }
    );

    describeMultiWS(
      "AND WHEN note id is missing",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          const note = await NoteTestUtilsV4.createNote({
            fname: FNAME,
            vault: vaults[0],
            wsRoot,
          });
          // Rewrite the file to have id missing in frontmatter
          const path = NoteUtils.getFullPath({ note, wsRoot });
          await writeFile(
            path,
            ["---", "updated: 234", "created: 123", "---"].join("\n")
          );
        },
      },
      () => {
        test("THEN show frontmatter missing warning", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const { allWarnings } = (await updateDecorations(editor))!;
          expect(allWarnings!.length).toEqual(1);
          expect(
            AssertUtils.assertInString({
              body: allWarnings![0].message,
              match: ["id", "missing"],
            })
          );
        });
      }
    );

    describeMultiWS("AND frontmatter is not visible", {}, () => {
      before(async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        const note = await NoteTestUtilsV4.createNoteWithEngine({
          fname: "foo",
          vault: vaults[0],
          wsRoot,
          engine,
        });
        // Rewrite the file to have id missing in frontmatter
        const path = NoteUtils.getFullPath({ note, wsRoot });
        await writeFile(
          path,
          ["---", "updated: 234", "created: 123", "---"]
            .join("\n")
            .concat("\n".repeat(200))
        );

        const editor = await WSUtils.openNote(note);
        editor.revealRange(new vscode.Range(200, 0, 200, 0));
      });

      test("THEN still warns for frontmatter issues", async () => {
        const { allWarnings } = (await updateDecorations(
          VSCodeUtils.getActiveTextEditorOrThrow()
        ))!;
        expect(allWarnings!.length).toEqual(1);
        expect(
          AssertUtils.assertInString({
            body: allWarnings![0].message,
            match: ["id", "missing"],
          })
        );
      });

      runTestButSkipForWindows()("", () => {
        test("THEN don't warn for schemas", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const engine = ExtensionProvider.getEngine();
          const schema = engine.schemas.root;
          const schemaFile = path.join(
            wsRoot,
            schema.vault.fsPath,
            `${schema.fname}.schema.yml`
          );
          const schemaURI = vscode.Uri.parse(schemaFile);
          const editor = await VSCodeUtils.openFileInEditor(schemaURI);

          const { allDecorations, allWarnings } = (await updateDecorations(
            editor!
          ))!;

          expect(allWarnings).toEqual(undefined);
          expect(allDecorations).toEqual(undefined);
        });
      });
    });
  });
});
Example #27
Source File: image-compressor.ts    From image-optimizer with MIT License 4 votes vote down vote up
#processFile = (file: DroppedFile, output: string) => {
    const originalSize = getFileSize(file.path)

    return new Promise<void>((resolve, reject) => {
      switch (file.type) {
        case MIME_TYPE_ENUM.jpg: {
          const { quality } = store.app.get('mozjpeg')

          let originalFile: string
          const isAddTempFile =
            !store.app.get('addToSubfolder') && !store.app.get('addMinSuffix')

          if (isAddTempFile) {
            originalFile = output + '.tmp'
            copyFileSync(file.path, originalFile)
          } else {
            originalFile = file.path
          }

          execFile(
            mozjpeg,
            ['-quality', `${quality}`, '-outfile', output, originalFile],
            err => {
              if (err) {
                console.log(err)
                reject(err)
              }

              const compressedSize = getFileSize(output)
              this.#sendToRenderer(file, originalSize, compressedSize)
              if (isAddTempFile) unlinkSync(originalFile)
              resolve()
            }
          )
          break
        }

        case MIME_TYPE_ENUM.png: {
          const { qualityMin, qualityMax } = store.app.get('pngquant')

          execFile(
            pngquant,
            [
              '--quality',
              `${qualityMin}-${qualityMax}`,
              '-fo',
              output,
              file.path
            ],
            err => {
              if (err) {
                console.log(err)
                reject(err)
              }

              const compressedSize = getFileSize(output)
              this.#sendToRenderer(file, originalSize, compressedSize)
              resolve()
            }
          )
          break
        }

        case MIME_TYPE_ENUM.gif: {
          execFile(gifsicle, ['-o', output, file.path], err => {
            if (err) {
              console.log(err)
              reject(err)
            }

            const compressedSize = getFileSize(output)
            this.#sendToRenderer(file, originalSize, compressedSize)
            resolve()
          })
          break
        }

        case MIME_TYPE_ENUM.svg: {
          readFile(file.path, (err, buffer) => {
            if (err) {
              console.log(err)
              reject(err)
            }

            const { data } = svg.optimize(buffer)
            writeFile(output, data, err => {
              if (err) console.log(err)

              const compressedSize = getFileSize(output)
              this.#sendToRenderer(file, originalSize, compressedSize)
              resolve()
            })
          })
          break
        }
      }
    })
  }
Example #28
Source File: ui5-model-spec.ts    From ui5-language-assistant with Apache License 2.0 4 votes vote down vote up
describe("the UI5 language assistant ui5 model", () => {
  // The default timeout is 2000ms and getSemanticModel can take ~3000-5000ms
  const GET_MODEL_TIMEOUT = 10000;
  const VERSION = "1.71.14";
  const NO_CACHE_FOLDER = undefined;

  function assertSemanticModel(ui5Model: UI5SemanticModel): void {
    expect(ui5Model.version).to.equal(VERSION);

    expect(Object.keys(ui5Model.classes).length).to.be.greaterThan(200);
    expect(Object.keys(ui5Model.namespaces).length).to.be.greaterThan(200);
    expect(Object.keys(ui5Model.interfaces).length).to.be.greaterThan(30);
    expect(Object.keys(ui5Model.functions).length).to.be.greaterThan(30);
    expect(Object.keys(ui5Model.enums).length).to.be.greaterThan(200);
    expect(Object.keys(ui5Model.typedefs).length).to.be.greaterThan(10);

    expect(Object.keys(ui5Model.classes)).to.include("sap.m.List");
    expect(Object.keys(ui5Model.namespaces)).to.include("sap.m");
    expect(Object.keys(ui5Model.interfaces)).to.include("sap.f.ICard");
    expect(Object.keys(ui5Model.functions)).to.include(
      "module:sap/base/assert"
    );
    expect(Object.keys(ui5Model.enums)).to.include("sap.m.ButtonType");
    expect(Object.keys(ui5Model.typedefs)).to.include("sap.ui.fl.Selector");

    // Dist layer
    expect(Object.keys(ui5Model.classes)).to.include("sap.ui.vk.Camera");
    expect(Object.keys(ui5Model.namespaces)).to.include("sap.apf.base");
    expect(Object.keys(ui5Model.enums)).to.include(
      "sap.ca.ui.charts.ChartSelectionMode"
    );
  }

  it("will get UI5 semantic model", async () => {
    const ui5Model = await getSemanticModel(NO_CACHE_FOLDER);
    assertSemanticModel(ui5Model);
  }).timeout(GET_MODEL_TIMEOUT);

  it("doesn't fail if a file cannot be fetched", async () => {
    const ui5Model = await getSemanticModelWithFetcher(async (url: string) => {
      return {
        ok: false,
        json: (): never => {
          throw new Error(`Cannot read from ${url}`);
        },
      };
    }, NO_CACHE_FOLDER);
    expect(ui5Model).to.exist;
  });

  describe("with cache", () => {
    describe("cache in temp dir", () => {
      let cachePath: string;
      beforeEach(async () => {
        ({ path: cachePath } = await tempDir());
      });

      afterEach(async () => {
        // The temp folder will contain files at the end so we remove it
        // with rimraf instead of calling cleanup()
        rimrafSync(cachePath);
      });

      it("caches the model the first time getSemanticModel is called", async () => {
        const ui5Model = await getSemanticModel(cachePath);
        assertSemanticModel(ui5Model);

        // Check the files were created in the folder
        const files = await readdir(cachePath);
        expect(files).to.not.be.empty;

        // Call getSemanticModel again with the same path and check it doesn't try to read from the URL
        let fetcherCalled = false;
        const ui5ModelFromCache = await getSemanticModelWithFetcher(
          (url: string): never => {
            fetcherCalled = true;
            throw new Error(
              `The files should be taken from the cache, got call for ${url}`
            );
          },
          cachePath
        );
        expect(fetcherCalled).to.be.false;
        // Make sure it's not the model itself that is cached
        expect(ui5ModelFromCache).to.not.equal(ui5Model);
        // Check we got the same result (we can't use deep equal so the check is shallow)
        forEach(ui5Model, (value, key) => {
          if (isPlainObject(value)) {
            expect(
              Object.keys(
                ui5ModelFromCache[key as keyof UI5SemanticModel] as Record<
                  string,
                  unknown
                >
              )
            ).to.deep.equalInAnyOrder(
              Object.keys(value as Record<string, unknown>)
            );
          }
        });
        assertSemanticModel(ui5ModelFromCache);
      }).timeout(GET_MODEL_TIMEOUT);

      it("doesn't fail when file cannot be written to the cache", async () => {
        // Create a folder with the file name so the file will not be written
        const cacheFilePath = getCacheFilePath(
          getCacheFolder(cachePath, VERSION),
          "sap.m"
        );
        expectExists(cacheFilePath, "cacheFilePath");
        await mkdirs(cacheFilePath);

        const ui5Model = await getSemanticModel(cachePath);
        expect(ui5Model).to.exist;
        // Check we still got the sap.m library data
        expect(Object.keys(ui5Model.namespaces)).to.contain("sap.m");
        expect(ui5Model.namespaces["sap.m"].library).to.equal("sap.m");
      }).timeout(GET_MODEL_TIMEOUT);

      it("doesn't fail when file cannot be read from the cache", async () => {
        // Create a file with non-json content so the file will not be deserialized
        const cacheFolder = getCacheFolder(cachePath, VERSION);
        await mkdirs(cacheFolder);
        const cacheFilePath = getCacheFilePath(cacheFolder, "sap.m");
        expectExists(cacheFilePath, "cacheFilePath");
        await writeFile(cacheFilePath, "not json");

        const ui5Model = await getSemanticModel(cachePath);
        expect(ui5Model).to.exist;
        // Check we still got the sap.m library data
        expect(Object.keys(ui5Model.namespaces)).to.contain("sap.m");
        expect(ui5Model.namespaces["sap.m"].library).to.equal("sap.m");
      }).timeout(GET_MODEL_TIMEOUT);
    });

    describe("cache path is a file", async () => {
      let cachePath: string;
      let cleanup: () => Promise<void>;
      beforeEach(async () => {
        ({ path: cachePath, cleanup } = await tempFile());
      });

      afterEach(async () => {
        await cleanup();
      });

      it("does not cache the model", async () => {
        const ui5Model = await getSemanticModel(cachePath);
        assertSemanticModel(ui5Model);

        // Call getSemanticModel again with the same path and check it doesn't try to read from the URL
        let fetcherCalled = false;
        await getSemanticModelWithFetcher(async (): Promise<FetchResponse> => {
          fetcherCalled = true;
          return {
            ok: true,
            json: async (): Promise<unknown> => {
              return {};
            },
          };
        }, cachePath);
        expect(fetcherCalled).to.be.true;
      }).timeout(GET_MODEL_TIMEOUT);
    });
  });
});
Example #29
Source File: ResourceApi.test.ts    From joplin-utils with MIT License 4 votes vote down vote up
describe('test ResourceApi', () => {
  let id: string
  beforeAll(async () => {
    id = (await createTestResource()).id
  })
  afterAll(async () => {
    await resourceApi.remove(id)
  })
  const tempPath = path.resolve(__dirname, '.temp')
  beforeEach(async () => {
    await remove(tempPath)
    await mkdirp(tempPath)
  })
  it('test list', async () => {
    const res = await resourceApi.list({ fields: ['id', 'title'] })
    console.log(res)
    expect(res.items.length).toBeGreaterThan(0)
  })
  it('test get', async () => {
    const res = await resourceApi.get(id)
    console.log(res)
    expect(res.id).toBe(id)
  })
  /**
   * TODO 一个官方未修复的 bug,参考:https://github.com/laurent22/joplin/issues/4575
   */
  it.skip('test get filename', async () => {
    const res = await resourceApi.get(id, ['id', 'filename'])
    console.log(res)
    expect(res.filename).not.toBe('')
  })

  describe('diff fetch and axios', () => {
    const path = resolve(__dirname, '../resource/resourcesByFileId.png')
    it('test create', async () => {
      const title = 'image title'
      const json = await resourceApi.create({
        title,
        data: createReadStream(path),
      })
      expect(json.title).toBe(title)
    })
  })
  describe('test update', () => {
    it('basic example', async () => {
      const title = `new title ${Date.now()}`
      const updateRes = await resourceApi.update({ id, title })
      expect(updateRes.title).toBe(title)
    })
    it('update file', async () => {
      const content = 'test'
      const txtPath = path.resolve(tempPath, 'test.txt')
      await writeFile(txtPath, content)
      await resourceApi.update({ id, data: createReadStream(txtPath) })
      const res = await resourceApi.fileByResourceId(id)
      expect(res.toString()).toBe(content)
    })
    it('update properties and file', async () => {
      const title = `new title ${Date.now()}`
      const content = 'test'
      const txtPath = path.resolve(tempPath, 'test.txt')
      await writeFile(txtPath, content)
      const updateRes = await resourceApi.update({ id, title, data: createReadStream(txtPath) })
      expect(updateRes.title).toBe(title)
      const res = await resourceApi.fileByResourceId(id)
      expect(res.toString()).toBe(content)
    })
    it('update file only', async () => {
      const content = 'test'
      const txtPath = path.resolve(tempPath, 'test.txt')
      await writeFile(txtPath, content)
      const { title } = await resourceApi.get(id)
      await resourceApi.update({ id, data: createReadStream(txtPath) })
      const res = await resourceApi.fileByResourceId(id)
      expect(res.toString()).toBe(content)
      expect((await resourceApi.get(id)).title).toBe(title)
    })
  })
  /**
   * 已知错误
   * https://discourse.joplinapp.org/t/pre-release-v2-8-is-now-available-updated-14-april/25158/10?u=rxliuli
   */
  it.skip('test remove ', async () => {
    const id = (await createTestResource()).id
    await resourceApi.remove(id)
    await expect(resourceApi.get(id)).rejects.toThrowError()
  })
  it('test fileByResourceId', async () => {
    const res = await resourceApi.fileByResourceId(id)
    console.log(typeof res)
    const path = resolve(tempPath, 'resourcesByFileId.png')
    await writeFile(path, res)
    expect(pathExistsSync(path)).toBeTruthy()
  })
  it('test to get the size of the attachment resource', async () => {
    const id = (await createTestResource()).id
    const res = await resourceApi.get(id, ['id', 'title', 'size'])
    const stats = await stat(path.resolve(__dirname, '../resource/resourcesByFileId.png'))
    expect(res.size).toEqual(stats.size)
  })
})