diff --git a/package.json b/package.json index 53d252ec..d4c4e580 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@pinia/nuxt": "^0.4.5", "@types/fs-extra": "^9.0.13", "@types/js-yaml": "^4.0.5", + "@types/prettier": "^2.7.1", "@types/sanitize-html": "^2.6.2", "@types/wicg-file-system-access": "^2020.9.5", "@unocss/nuxt": "^0.46.5", @@ -45,12 +46,14 @@ "parse5": "^7.1.2", "pinia": "^2.0.26", "postcss-nested": "^6.0.0", + "prettier": "^2.8.0", "rollup-plugin-node-polyfills": "^0.2.1", "sanitize-html": "^2.7.3", "shiki": "^0.11.1", "theme-vitesse": "^0.6.0", "typescript": "^4.9.3", "ufo": "^1.0.0", + "unplugin-auto-import": "^0.11.5", "vitest": "^0.25.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e875ff5..2ba8c630 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,7 @@ specifiers: '@pinia/nuxt': ^0.4.5 '@types/fs-extra': ^9.0.13 '@types/js-yaml': ^4.0.5 + '@types/prettier': ^2.7.1 '@types/sanitize-html': ^2.6.2 '@types/wicg-file-system-access': ^2020.9.5 '@unocss/nuxt': ^0.46.5 @@ -30,12 +31,14 @@ specifiers: parse5: ^7.1.2 pinia: ^2.0.26 postcss-nested: ^6.0.0 + prettier: ^2.8.0 rollup-plugin-node-polyfills: ^0.2.1 sanitize-html: ^2.7.3 shiki: ^0.11.1 theme-vitesse: ^0.6.0 typescript: ^4.9.3 ufo: ^1.0.0 + unplugin-auto-import: ^0.11.5 vitest: ^0.25.3 devDependencies: @@ -48,6 +51,7 @@ devDependencies: '@pinia/nuxt': 0.4.5_typescript@4.9.3 '@types/fs-extra': 9.0.13 '@types/js-yaml': 4.0.5 + '@types/prettier': 2.7.1 '@types/sanitize-html': 2.6.2 '@types/wicg-file-system-access': 2020.9.5 '@unocss/nuxt': 0.46.5 @@ -68,12 +72,14 @@ devDependencies: parse5: 7.1.2 pinia: 2.0.26_typescript@4.9.3 postcss-nested: 6.0.0 + prettier: 2.8.0 rollup-plugin-node-polyfills: 0.2.1 sanitize-html: 2.7.3 shiki: 0.11.1 theme-vitesse: 0.6.0 typescript: 4.9.3 ufo: 1.0.0 + unplugin-auto-import: 0.11.5 vitest: 0.25.3 packages: @@ -195,6 +201,10 @@ packages: resolution: {integrity: sha512-sEYpyyKUPOew9QsXZ8feRVMzW6DWLviwOl+/ap06UQW02A8Srbc95CPHVm4eUbiBzBgD46eyIT+przv//KSSlQ==} dev: true + /@antfu/utils/0.7.0: + resolution: {integrity: sha512-tH38JQEFLOdvZJC32ZbPTvWOQzxEtOQh5jOqBPDLw8sxBr0PFF+f2Csgwb7mRpD0QB1xu+PDoAifIPiCNneeNA==} + dev: true + /@babel/code-frame/7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} @@ -1107,6 +1117,10 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/prettier/2.7.1: + resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} + dev: true + /@types/resolve/1.20.2: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} dev: true @@ -5968,6 +5982,12 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier/2.8.0: + resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /pretty-bytes/6.0.0: resolution: {integrity: sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==} engines: {node: ^14.13.1 || >=16.0.0} @@ -6973,6 +6993,25 @@ packages: - vite dev: true + /unplugin-auto-import/0.11.5: + resolution: {integrity: sha512-nvbL2AQwLRR8wbHpJ6L1EBVNmjN045RSedTa4NtsGRkSQFXkI1iKHs4dTqJwcKZsnFrZOAKtLPiN1/oQTObLZw==} + engines: {node: '>=14'} + peerDependencies: + '@vueuse/core': '*' + peerDependenciesMeta: + '@vueuse/core': + optional: true + dependencies: + '@antfu/utils': 0.7.0 + '@rollup/pluginutils': 5.0.2 + local-pkg: 0.4.2 + magic-string: 0.26.7 + unimport: 1.0.1 + unplugin: 1.0.0 + transitivePeerDependencies: + - rollup + dev: true + /unplugin-combine/0.2.8: resolution: {integrity: sha512-Z38AC/TEjXbVyZ5HjVqo+lADj0/dcfwWC0Z4y0LNhybJzJQwmcMxm+ZsqHY3faauj4YigmlRMdptR5JEW9RuLg==} engines: {node: '>=14.19.0'} diff --git a/tests/__snapshots__/content.test.ts.snap b/tests/__snapshots__/content.test.ts.snap index 549d3f8a..c2e4059e 100644 --- a/tests/__snapshots__/content.test.ts.snap +++ b/tests/__snapshots__/content.test.ts.snap @@ -1,6 +1,49 @@ // Vitest Snapshot v1 -exports[`rich-content > plain 1`] = ` -"Hello -World" +exports[`rich-content > code frame 1`] = ` +"

Testing code block

+
+import { useMouse, usePreferredDark } from '@vueuse/core'
+// tracks mouse position
+const { x, y } = useMouse()
+// is the user prefers dark theme
+const isDark = usePreferredDark()
+

+" +`; + +exports[`rich-content > custom emoji 1`] = ` +"Daniel Roe +\\"nuxt\\" +" +`; + +exports[`rich-content > empty 1`] = `""`; + +exports[`rich-content > link + mention 1`] = ` +"

+ Happy 🤗 we’re now using + + (migrated from chai+mocha) + https://github.com/ayoayco/astro-reactive-library/pull/203 +

+" `; diff --git a/tests/content.test.ts b/tests/content.test.ts index 27f00df0..142563c6 100644 --- a/tests/content.test.ts +++ b/tests/content.test.ts @@ -1,21 +1,81 @@ import type { Emoji } from 'masto' -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { renderToString } from 'vue/server-renderer' +import { format } from 'prettier' import { contentToVNode } from '~/composables/content' +describe('rich-content', () => { + it('empty', async () => { + const { formatted } = await render('') + expect(formatted).toMatchSnapshot() + }) + + it('link + mention', async () => { + // https://fosstodon.org/@ayo/109383002937620723 + const { formatted } = await render('

Happy 🤗 we’re now using @vitest (migrated from chai+mocha) github.com/ayoayco/astro-react

') + expect(formatted).toMatchSnapshot() + }) + + it('custom emoji', async () => { + const { formatted } = await render('Daniel Roe :nuxt:', { + nuxt: { + shortcode: 'nuxt', + url: 'https://media.mas.to/masto-public/cache/custom_emojis/images/000/288/667/original/c96ba3cb0e0e1eac.png', + staticUrl: 'https://media.mas.to/masto-public/cache/custom_emojis/images/000/288/667/static/c96ba3cb0e0e1eac.png', + visibleInPicker: true, + }, + }) + expect(formatted).toMatchSnapshot() + }) + + it('code frame', async () => { + // https://mas.to/@antfu/109396489827394721 + const { formatted } = await render('

Testing code block

```ts
import { useMouse, usePreferredDark } from '@vueuse/core'

// tracks mouse position
const { x, y } = useMouse()

// is the user prefers dark theme
const isDark = usePreferredDark()
```

') + expect(formatted).toMatchSnapshot() + }) +}) + async function render(content: string, emojis?: Record) { const vnode = contentToVNode(content, emojis) const html = (await renderToString(vnode)) .replace(//g, '') + const formatted = format(html, { + parser: 'html', + }) + return { vnode, html, + formatted, } } -describe('rich-content', () => { - it('plain', async () => { - const { html } = await render('Hello\nWorld') - expect(html).toMatchSnapshot() - }) +// mocks +vi.mock('vue-router', () => { + return { + RouterLink: defineComponent((attrs) => { + return () => h('a', attrs) + }), + } +}) + +vi.mock('../components/content/ContentCode.vue', () => { + return { + default: defineComponent({ + props: { + code: { + type: String, + required: true, + }, + lang: { + type: String, + required: true, + }, + }, + setup(props) { + const raw = computed(() => decodeURIComponent(props.code).replace(/'/g, '\'')) + return () => h('pre', { lang: props.lang }, raw.value) + }, + }), + } }) diff --git a/vitest.config.ts b/vitest.config.ts index f335e362..5cd8a6d4 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,6 +1,7 @@ import { resolve } from 'path' import { defineConfig } from 'vitest/config' import Vue from '@vitejs/plugin-vue' +import AutoImport from 'unplugin-auto-import/vite' export default defineConfig({ resolve: { @@ -10,5 +11,15 @@ export default defineConfig({ }, plugins: [ Vue(), + AutoImport({ + dts: false, + imports: [ + 'vue', + '@vueuse/core', + ], + dirs: [ + 'composables', + ], + }), ], })