feat: wire global runtime injection and playground
This commit is contained in:
@@ -7,9 +7,23 @@ describe('ssr', async () => {
|
||||
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)),
|
||||
})
|
||||
|
||||
it('renders the index page', async () => {
|
||||
// Get response to a server-rendered page with `$fetch`.
|
||||
it('renders the allowed branches when permissions pass', async () => {
|
||||
const html = await $fetch('/')
|
||||
expect(html).toContain('<div>basic</div>')
|
||||
expect(html).toContain('<h1>basic</h1>')
|
||||
expect(html).toContain('id="can-view"')
|
||||
expect(html).toContain('id="can-edit"')
|
||||
expect(html).toContain('Creation contrat')
|
||||
})
|
||||
|
||||
it('falls back to the `v-cannot` branch when the permission is missing', async () => {
|
||||
const html = await $fetch('/')
|
||||
expect(html).not.toContain('id="can-delete"')
|
||||
expect(html).toContain('id="cannot-delete"')
|
||||
expect(html).toContain('Suppression interdite')
|
||||
})
|
||||
|
||||
it('exposes the can proxy globally for template usage', async () => {
|
||||
const html = await $fetch('/')
|
||||
expect(html).toMatch(/id="path-display"[\s\S]*employee\.view/)
|
||||
})
|
||||
})
|
||||
|
||||
80
test/fixtures/basic/app.vue
vendored
80
test/fixtures/basic/app.vue
vendored
@@ -1,6 +1,82 @@
|
||||
<template>
|
||||
<div>basic</div>
|
||||
<main>
|
||||
<h1>basic</h1>
|
||||
|
||||
<section>
|
||||
<button
|
||||
id="can-view"
|
||||
v-can="canProxy.employee.view"
|
||||
>
|
||||
Voir
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="isReady"
|
||||
id="can-edit"
|
||||
v-can="canProxy.employee.edit"
|
||||
>
|
||||
Editer
|
||||
</button>
|
||||
<p
|
||||
id="cannot-edit"
|
||||
v-cannot
|
||||
>
|
||||
Refus
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<template v-if="showContracts">
|
||||
<p v-can="canProxy.contract.create">
|
||||
Creation contrat
|
||||
</p>
|
||||
<p v-cannot>
|
||||
Pas de creation
|
||||
</p>
|
||||
</template>
|
||||
<p v-else>
|
||||
Section contrats masquee, aucune directive appliquee.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Suppression</h2>
|
||||
<button
|
||||
id="can-delete"
|
||||
v-can="canProxy.employee.delete"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
<p
|
||||
id="cannot-delete"
|
||||
v-cannot
|
||||
>
|
||||
Suppression interdite
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p id="path-display">
|
||||
{{ String(canProxy.employee.view) }}
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const isReady = true
|
||||
const showContracts = true
|
||||
interface FixturePermissions {
|
||||
employee: {
|
||||
view: boolean
|
||||
edit: boolean
|
||||
delete: boolean
|
||||
}
|
||||
contract: {
|
||||
create: boolean
|
||||
}
|
||||
}
|
||||
|
||||
const nuxtApp = useNuxtApp()
|
||||
const canProxy = nuxtApp.$can as unknown as FixturePermissions
|
||||
</script>
|
||||
|
||||
11
test/fixtures/basic/nuxt.config.ts
vendored
11
test/fixtures/basic/nuxt.config.ts
vendored
@@ -1,7 +1,14 @@
|
||||
import MyModule from '../../../src/module'
|
||||
import NuxtCan from '../../../src/module'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
MyModule,
|
||||
NuxtCan,
|
||||
],
|
||||
nuxtCan: {
|
||||
permissions: {
|
||||
employee: ['view', 'edit', 'delete'],
|
||||
contract: ['create'],
|
||||
},
|
||||
canFunctionImport: '~/permissions/__can__',
|
||||
},
|
||||
})
|
||||
|
||||
10
test/fixtures/basic/permissions/__can__.ts
vendored
Normal file
10
test/fixtures/basic/permissions/__can__.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
const granted = new Set([
|
||||
'employee.view',
|
||||
'employee.edit',
|
||||
'contract.create',
|
||||
])
|
||||
|
||||
export function __can__(path: string[]) {
|
||||
const key = Array.isArray(path) ? path.join('.') : String(path)
|
||||
return granted.has(key)
|
||||
}
|
||||
62
test/transform-can.test.ts
Normal file
62
test/transform-can.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
|
||||
import { transformCan } from '../src/runtime/transformer/transform-can'
|
||||
|
||||
const TEST_FILE = `${process.cwd()}/components/Test.vue`
|
||||
|
||||
const buildSFC = (template: string) => `<template>\n${template}\n</template>`
|
||||
|
||||
const runTransform = (template: string) => {
|
||||
const result = transformCan({ code: buildSFC(template), id: TEST_FILE })
|
||||
return result?.code ?? ''
|
||||
}
|
||||
|
||||
describe('transformCan', () => {
|
||||
it('injects __can__ guards and a matching v-cannot block', () => {
|
||||
const code = runTransform(`
|
||||
<button v-can="can.employee.view">Voir</button>
|
||||
<p v-cannot>Refus</p>
|
||||
`)
|
||||
|
||||
expect(code).toContain(`v-if="__can__(['employee', 'view'])"`)
|
||||
expect(code).toContain(`v-if="!(__can__(['employee', 'view']))"`)
|
||||
})
|
||||
|
||||
it('merges existing v-if expressions with the generated guard', () => {
|
||||
const code = runTransform(`
|
||||
<div v-if="isReady" v-can="can.contract.create" />
|
||||
`)
|
||||
|
||||
expect(code).toContain(`v-if="(isReady) && __can__(['contract', 'create'])"`)
|
||||
})
|
||||
|
||||
it('throws when v-cannot is used without a preceding v-can', () => {
|
||||
const exec = () => transformCan({
|
||||
code: buildSFC('<p v-cannot>Denied</p>'),
|
||||
id: TEST_FILE,
|
||||
})
|
||||
|
||||
expect(exec).toThrow(/must immediately follow its `v-can`/)
|
||||
})
|
||||
|
||||
it('throws when the expression does not start with can.*', () => {
|
||||
const exec = () => transformCan({
|
||||
code: buildSFC('<button v-can="permissions.employee.view" />'),
|
||||
id: TEST_FILE,
|
||||
})
|
||||
|
||||
expect(exec).toThrow(/expressions must start with `can\.`/)
|
||||
})
|
||||
|
||||
it('throws when v-can is added to a v-else branch', () => {
|
||||
const exec = () => transformCan({
|
||||
code: buildSFC(`
|
||||
<div v-if="ready"></div>
|
||||
<div v-else v-can="can.employee.view"></div>
|
||||
`),
|
||||
id: TEST_FILE,
|
||||
})
|
||||
|
||||
expect(exec).toThrow(/cannot be used on `v-else`/)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user