@prisma/language-server
Version:
Prisma Language Server
1,742 lines (1,669 loc) • 113 kB
text/typescript
import dedent from 'ts-dedent'
import { expect, describe, test } from 'vitest'
import { CompletionList, CompletionParams, CompletionTriggerKind, CompletionItemKind } from 'vscode-languageserver'
import { handleCompletionRequest } from '../../lib/MessageHandler'
import { PrismaSchema } from '../../lib/Schema'
import { findCursorPosition, CURSOR_CHARACTER } from '../helper'
import { TextDocument } from 'vscode-languageserver-textdocument'
type DatasourceProvider = 'sqlite' | 'postgresql' | 'mysql' | 'mongodb' | 'sqlserver' | 'cockroachdb'
const baseSchema = (provider?: DatasourceProvider, previewFeatures?: string[]) => {
if (!provider && previewFeatures?.length === 0) {
throw new Error(`provider and/or previewFeatures is required.`)
}
let base = ''
if (provider) {
base = /* Prisma */ `
datasource db {
provider = "${provider}"
url = env("DATABASE_URL")
}`
}
if (previewFeatures?.length) {
base += /* Prisma */ `
generator js {
provider = "prisma-client-js"
previewFeatures = ["${previewFeatures.join('","')}"]
}`
}
return dedent(base)
}
function assertCompletion({
provider,
previewFeatures,
schema,
expected,
}: {
provider?: DatasourceProvider
previewFeatures?: string[]
schema: string
expected: CompletionList
}): void {
// Remove indentation
schema = dedent(schema)
if (provider || previewFeatures) {
schema = `
${baseSchema(provider, previewFeatures)}
${schema}
`
}
const position = findCursorPosition(schema)
const document: TextDocument = TextDocument.create(
'file:///completions/none.prisma',
'prisma',
1,
schema.replace(CURSOR_CHARACTER, ''),
)
const completionParams: CompletionParams = {
textDocument: document,
position,
context: {
triggerKind: CompletionTriggerKind.Invoked,
},
}
const completionResult: CompletionList | undefined = handleCompletionRequest(
PrismaSchema.singleFile(document),
document,
completionParams,
)
expect(completionResult).not.toBeUndefined()
expect(
completionResult?.isIncomplete,
`Line ${position.line} - Character ${position.character}
Expected isIncomplete to be '${expected.isIncomplete}' but got '${completionResult?.isIncomplete}'`,
).toStrictEqual(expected.isIncomplete)
expect(
completionResult?.items.map((item) => item.label),
`Line ${position.line} - Character ${position.character}
mapped items => item.label`,
).toStrictEqual(expected.items.map((item) => item.label))
expect(
completionResult?.items.map((item) => item.kind),
`Line ${position.line} - Character ${position.character}
mapped items => item.kind`,
).toStrictEqual(expected.items.map((item) => item.kind))
// TODO: This is missing the output of `expected.items` so one can compare
expect(
completionResult?.items.length,
`Line ${position.line} - Character ${position.character}
Expected ${expected.items.length} suggestions and got ${completionResult?.items.length}: ${JSON.stringify(
completionResult?.items,
undefined,
2,
)}`,
).toStrictEqual(expected.items.length)
}
describe('Completions', function () {
// used in more than 1 describe
//#region types
const fieldProvider = {
label: 'provider',
kind: CompletionItemKind.Field,
}
const staticValueTrue = {
label: 'true',
kind: CompletionItemKind.Value,
}
const staticValueFalse = {
label: 'false',
kind: CompletionItemKind.Value,
}
const fieldsProperty = {
label: 'fields',
kind: CompletionItemKind.Property,
}
const mapProperty = {
label: 'map',
kind: CompletionItemKind.Property,
}
const sortProperty = {
label: 'sort',
kind: CompletionItemKind.Property,
}
const nameProperty = {
label: 'name',
kind: CompletionItemKind.Property,
}
//#endregion
describe('BASE BLOCKS', () => {
test('Diagnoses block type suggestions for empty file', () => {
assertCompletion({
schema: /* Prisma */ `|`,
expected: {
isIncomplete: false,
items: [
{ label: 'datasource', kind: CompletionItemKind.Class },
{ label: 'generator', kind: CompletionItemKind.Class },
{ label: 'model', kind: CompletionItemKind.Class },
{ label: 'enum', kind: CompletionItemKind.Class },
],
},
})
})
test('Diagnoses block type suggestions with sqlite as provider', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
provider = "sqlite"
}
|
`,
expected: {
isIncomplete: false,
items: [
{ label: 'datasource', kind: CompletionItemKind.Class },
{ label: 'generator', kind: CompletionItemKind.Class },
{ label: 'model', kind: CompletionItemKind.Class },
],
},
})
})
test('Diagnoses block type suggestions with mongodb as provider', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
provider = "mongodb"
}
|
`,
expected: {
isIncomplete: false,
items: [
{ label: 'datasource', kind: CompletionItemKind.Class },
{ label: 'generator', kind: CompletionItemKind.Class },
{ label: 'model', kind: CompletionItemKind.Class },
{ label: 'enum', kind: CompletionItemKind.Class },
{ label: 'type', kind: CompletionItemKind.Class },
],
},
})
})
test('Diagnoses block type suggestions for view preview', () => {
assertCompletion({
schema: /* Prisma */ `
generator client {
provider = "prisma-client-js"
// ! Assures we are reading the correct previewFeatures section.
// previewFeatures = []
previewFeatures = ["views"]
}
|
`,
expected: {
isIncomplete: false,
items: [
{ label: 'datasource', kind: CompletionItemKind.Class },
{ label: 'generator', kind: CompletionItemKind.Class },
{ label: 'model', kind: CompletionItemKind.Class },
{ label: 'enum', kind: CompletionItemKind.Class },
{ label: 'view', kind: CompletionItemKind.Class },
],
},
})
})
})
describe('DATABASE BLOCK', () => {
const fieldUrl = { label: 'url', kind: CompletionItemKind.Field }
const fieldDirectUrl = { label: 'directUrl', kind: CompletionItemKind.Field }
const fieldShadowDatabaseUrl = {
label: 'shadowDatabaseUrl',
kind: CompletionItemKind.Field,
}
const fieldRelationMode = {
label: 'relationMode',
kind: CompletionItemKind.Field,
}
const fieldPostgresqlExtensions = {
label: 'extensions',
kind: CompletionItemKind.Field,
}
const fieldSchemas = {
label: 'schemas',
kind: CompletionItemKind.Field,
}
const sqlite = { label: 'sqlite', kind: CompletionItemKind.Constant }
const mysql = { label: 'mysql', kind: CompletionItemKind.Constant }
const postgresql = {
label: 'postgresql',
kind: CompletionItemKind.Constant,
}
const sqlserver = {
label: 'sqlserver',
kind: CompletionItemKind.Constant,
}
const mongodb = { label: 'mongodb', kind: CompletionItemKind.Constant }
const cockroachdb = {
label: 'cockroachdb',
kind: CompletionItemKind.Constant,
}
const relationModeForeignKeys = {
label: 'foreignKeys',
kind: CompletionItemKind.Field,
}
const relationModePrisma = {
label: 'prisma',
kind: CompletionItemKind.Field,
}
const relationModeForeignKeysWithQuotes = {
label: '"foreignKeys"',
kind: CompletionItemKind.Field,
}
const relationModePrismaWithQuotes = {
label: '"prisma"',
kind: CompletionItemKind.Field,
}
const quotationMarks = {
label: '""',
kind: CompletionItemKind.Property,
}
const env = { label: 'env()', kind: CompletionItemKind.Property }
test('Diagnoses datasource field suggestions in empty block', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
|
}`,
expected: {
isIncomplete: false,
items: [fieldProvider, fieldUrl, fieldShadowDatabaseUrl, fieldDirectUrl, fieldRelationMode],
},
})
})
test('Diagnoses datasource field suggestions with existing field', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
provider = "sqlite"
|
}`,
expected: {
isIncomplete: false,
items: [fieldProvider, fieldUrl, fieldShadowDatabaseUrl, fieldDirectUrl, fieldRelationMode],
},
})
assertCompletion({
schema: /* Prisma */ `
datasource db {
url = env("DATABASE_URL")
|
}`,
expected: {
isIncomplete: false,
items: [fieldProvider, fieldUrl, fieldShadowDatabaseUrl, fieldDirectUrl, fieldRelationMode],
},
})
})
test('url = env("|")', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
url = |
}`,
expected: {
isIncomplete: false,
items: [env, quotationMarks],
},
})
assertCompletion({
schema: /* Prisma */ `
datasource db {
url = env("|")
}`,
expected: {
isIncomplete: false,
items: [
{
label: 'DATABASE_URL',
kind: CompletionItemKind.Constant,
},
],
},
})
})
test('shadowDatabaseUrl = env("|")', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
url = |
}`,
expected: {
isIncomplete: false,
items: [env, quotationMarks],
},
})
assertCompletion({
schema: /* Prisma */ `
datasource db {
shadowDatabaseUrl = env("|")
}`,
expected: {
isIncomplete: false,
items: [
{
label: 'SHADOW_DATABASE_URL',
kind: CompletionItemKind.Constant,
},
],
},
})
})
test('directUrl = env("|")', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
url = |
}`,
expected: {
isIncomplete: false,
items: [env, quotationMarks],
},
})
assertCompletion({
schema: /* Prisma */ `
datasource db {
directUrl = env("|")
}`,
expected: {
isIncomplete: false,
items: [
{
label: 'DIRECT_URL',
kind: CompletionItemKind.Constant,
},
],
},
})
})
test('Diagnoses field extensions availability', () => {
assertCompletion({
schema: /* Prisma */ `
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
|
}
`,
expected: {
isIncomplete: false,
items: [fieldShadowDatabaseUrl, fieldDirectUrl, fieldRelationMode, fieldPostgresqlExtensions],
},
})
})
test('Diagnoses field schemas', () => {
assertCompletion({
schema: /* Prisma */ `
generator client {
provider = "prisma-client-js"
previewFeatures = ["multiSchema"]
}
datasource db {
provider = "cockroachdb"
url = env("DATABASE_URL")
|
}
`,
expected: {
isIncomplete: false,
items: [fieldShadowDatabaseUrl, fieldDirectUrl, fieldRelationMode, fieldSchemas],
},
})
})
test('provider = "|"', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
provider = "|"
}`,
expected: {
isIncomplete: true,
items: [mysql, postgresql, sqlite, sqlserver, mongodb, cockroachdb],
},
})
})
test('provider = |', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
provider = |
}`,
expected: {
isIncomplete: true,
items: [quotationMarks],
},
})
})
test('relationMode = "|"', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
provider = "sqlite"
relationMode = "|"
}`,
expected: {
isIncomplete: false,
items: [relationModeForeignKeys, relationModePrisma],
},
})
})
test('relationMode = |', () => {
assertCompletion({
schema: /* Prisma */ `
datasource db {
relationMode = |
}`,
expected: {
isIncomplete: false,
items: [relationModeForeignKeysWithQuotes, relationModePrismaWithQuotes],
},
})
})
})
describe('GENERATOR BLOCK', () => {
// fieldProvider defined above already
//#region types
const fieldOutput = { label: 'output', kind: CompletionItemKind.Field }
const fieldBinaryTargets = {
label: 'binaryTargets',
kind: CompletionItemKind.Field,
}
const fieldPreviewFeatures = {
label: 'previewFeatures',
kind: CompletionItemKind.Field,
}
const fieldEngineType = {
label: 'engineType',
kind: CompletionItemKind.Field,
}
//#endregion
test('Diagnoses generator field suggestions in empty block', () => {
assertCompletion({
schema: /* Prisma */ `
generator gen {
|
}`,
expected: {
isIncomplete: false,
items: [fieldProvider, fieldPreviewFeatures, fieldOutput, fieldEngineType, fieldBinaryTargets],
},
})
})
test('Diagnoses generator field suggestions with existing fields', () => {
assertCompletion({
schema: /* Prisma */ `
generator gen {
provider = 'sqlite'
|
}`,
expected: {
isIncomplete: false,
items: [fieldPreviewFeatures, fieldOutput, fieldEngineType, fieldBinaryTargets],
},
})
assertCompletion({
schema: /* Prisma */ `
generator gen {
output = "node_modules/@prisma/client"
|
}`,
expected: {
isIncomplete: false,
items: [fieldProvider, fieldPreviewFeatures, fieldEngineType, fieldBinaryTargets],
},
})
})
test('engineType = |', () => {
assertCompletion({
schema: /* Prisma */ `
generator gen {
engineType = |
}`,
expected: {
isIncomplete: true,
items: [
{
label: '""',
kind: CompletionItemKind.Property,
},
],
},
})
})
test('engineType = "|"', () => {
assertCompletion({
schema: /* Prisma */ `
generator gen {
engineType = "|"
}`,
expected: {
isIncomplete: true,
items: [
{
label: 'library',
kind: CompletionItemKind.Constant,
},
{
label: 'binary',
kind: CompletionItemKind.Constant,
},
{
label: 'client',
kind: CompletionItemKind.Constant,
},
],
},
})
})
})
describe('BLOCK ATTRIBUTES', () => {
//#region types
const blockAttributeId = {
label: '@@id',
kind: CompletionItemKind.Property,
}
const blockAttributeMap = {
label: '@@map',
kind: CompletionItemKind.Property,
}
const blockAttributeUnique = {
label: '@@unique',
kind: CompletionItemKind.Property,
}
const blockAttributeIndex = {
label: '@@index',
kind: CompletionItemKind.Property,
}
const blockAttributeFulltextIndex = {
label: '@@fulltext',
kind: CompletionItemKind.Property,
}
const blockAttributeIgnore = {
label: '@@ignore',
kind: CompletionItemKind.Property,
}
const blockAttributeSchema = {
label: '@@schema',
kind: CompletionItemKind.Property,
}
const typeProperty = {
label: 'type',
kind: CompletionItemKind.Property,
}
const namespaceOne = {
label: 'one',
kind: CompletionItemKind.Property,
}
const namespaceTwo = {
label: 'two',
kind: CompletionItemKind.Property,
}
//#endregion
test('@@id([|])', () => {
assertCompletion({
schema: /* Prisma */ `
model ThirdUser {
firstName String
lastName String
isAdmin Boolean @default(false)
@@id([|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'firstName', kind: CompletionItemKind.Field },
{ label: 'lastName', kind: CompletionItemKind.Field },
{ label: 'isAdmin', kind: CompletionItemKind.Field },
],
},
})
})
describe('First in a line', () => {
test('Empty model', () => {
assertCompletion({
schema: /* Prisma */ `
model user {
|
}`,
expected: {
isIncomplete: false,
items: [
blockAttributeMap,
blockAttributeId,
blockAttributeUnique,
blockAttributeIndex,
blockAttributeIgnore,
],
},
})
})
test('Model', () => {
assertCompletion({
schema: /* Prisma */ `
model User {
firstName String
lastName String
email String @unique
isAdmin Boolean @default(false)
|
}`,
expected: {
isIncomplete: false,
items: [
blockAttributeMap,
blockAttributeId,
blockAttributeUnique,
blockAttributeIndex,
blockAttributeIgnore,
],
},
})
assertCompletion({
schema: /* Prisma */ `
model Post {
id Int @id @default()
email String? @unique
name String
|
}`,
expected: {
isIncomplete: false,
items: [blockAttributeMap, blockAttributeUnique, blockAttributeIndex, blockAttributeIgnore],
},
})
})
test('View', () => {
assertCompletion({
schema: /* Prisma */ `
view User {
firstName String
lastName String
email String @unique
isAdmin Boolean @default(false)
|
}
`,
expected: {
isIncomplete: false,
items: [
blockAttributeMap,
blockAttributeId,
blockAttributeUnique,
blockAttributeIndex,
blockAttributeIgnore,
],
},
})
})
describe('fullTextIndex', () => {
test('MySQL', () => {
assertCompletion({
provider: 'mysql',
previewFeatures: ['fullTextIndex'],
schema: /* Prisma */ `
model Fulltext {
id Int @id
title String @db.VarChar(255)
content String @db.Text
|
@@fulltext()
@@fulltext([title, content], )
}
`,
expected: {
isIncomplete: false,
items: [
blockAttributeMap,
// blockAttributeId,
blockAttributeUnique,
blockAttributeIndex,
blockAttributeFulltextIndex,
blockAttributeIgnore,
],
},
})
})
test('MongoDB', () => {
assertCompletion({
provider: 'mongodb',
previewFeatures: ['fullTextIndex'],
schema: /* Prisma */ `
model Fulltext {
id String @id @map("_id") @db.ObjectId
title String
content String
|
@@fulltext()
@@fulltext([title, content], )
}`,
expected: {
isIncomplete: false,
items: [
blockAttributeMap,
// blockAttributeId,
blockAttributeUnique,
blockAttributeIndex,
blockAttributeFulltextIndex,
blockAttributeIgnore,
],
},
})
})
test('PostgreSQL', () => {
assertCompletion({
provider: 'postgresql',
previewFeatures: ['fullTextIndex'],
schema: /* Prisma */ `
model A {
id Int @id
title String
content String
|
}
`,
expected: {
isIncomplete: false,
items: [
blockAttributeMap,
// blockAttributeId,
blockAttributeUnique,
blockAttributeIndex,
blockAttributeIgnore,
],
},
})
})
})
})
describe('@@unique()', function () {
describe('No provider', function () {
test('@@unique([|])', () => {
assertCompletion({
schema: /* Prisma */ `
model SecondUser {
firstName String
lastName String
isAdmin Boolean @default(false)
@@unique([|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'firstName', kind: CompletionItemKind.Field },
{ label: 'lastName', kind: CompletionItemKind.Field },
{ label: 'isAdmin', kind: CompletionItemKind.Field },
],
},
})
})
test('@@unique(fields: [|])', () => {
assertCompletion({
schema: /* Prisma */ `
model SecondUser {
firstName String
lastName String
isAdmin Boolean @default(false)
@@unique(fields: [|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'firstName', kind: CompletionItemKind.Field },
{ label: 'lastName', kind: CompletionItemKind.Field },
{ label: 'isAdmin', kind: CompletionItemKind.Field },
],
},
})
})
})
describe('MongoDB', function () {
test('@@unique([|])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@unique([|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
{ label: 'address', kind: CompletionItemKind.Field },
],
},
})
})
test('@@unique(fields: [|])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@unique(fields: [|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
{ label: 'address', kind: CompletionItemKind.Field },
],
},
})
})
})
})
describe('@@index()', function () {
describe('No provider', function () {
test('@@index([|])', () => {
assertCompletion({
schema: /* Prisma */ `
model ThirdUser {
firstName String
lastName String
isAdmin Boolean @default(false)
@@index([|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'firstName', kind: CompletionItemKind.Field },
{ label: 'lastName', kind: CompletionItemKind.Field },
{ label: 'isAdmin', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index(fields: [|])', () => {
assertCompletion({
schema: /* Prisma */ `
model ThirdUser {
firstName String
lastName String
isAdmin Boolean @default(false)
@@index(field: [|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'firstName', kind: CompletionItemKind.Field },
{ label: 'lastName', kind: CompletionItemKind.Field },
{ label: 'isAdmin', kind: CompletionItemKind.Field },
],
},
})
})
})
describe('MongoDB', function () {
test('@@index([|])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
{ label: 'address', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([a|])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
account Int
@@index([a|])
}`,
expected: {
isIncomplete: false,
items: [
// These are returned, but `onCompletionResolve` will only complete with the current match
// which means the completion will actually be
// address and account
// TODO create a test that shows that
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
{ label: 'address', kind: CompletionItemKind.Field },
{ label: 'account', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([address|])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
account Int
@@index([address|])
}`,
expected: {
isIncomplete: false,
items: [
// These are returned though the completion will actually be
// No suggestions
// TODO create a test that shows that
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
{ label: 'account', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([address,|])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([address,|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([address, |])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([address, |])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([address.|]) first position, with only one type', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([address.|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'street', kind: CompletionItemKind.Field },
{ label: 'number', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([address.|]) with composite type suggestion 1', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
alpha Alpha
}
type Alpha {
bravo Bravo
helloA Int
}
type Bravo {
something String
helloBravo Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([address.|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'street', kind: CompletionItemKind.Field },
{ label: 'number', kind: CompletionItemKind.Field },
{ label: 'alpha', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([address.a|]) with composite type suggestion 1', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
alpha Alpha
}
type Alpha {
bravo Bravo
helloA Int
}
type Bravo {
something String
helloBravo Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([address.a|])
}`,
expected: {
isIncomplete: false,
// TODO, see if we can have better suggestions here, should suggest `alpha`
items: [],
},
})
})
test('@@index([email,address.|]) with composite type suggestion, depth 1', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
alpha Alpha
}
type Alpha {
bravo Bravo
helloA Int
}
type Bravo {
something String
helloBravo Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([email,address.|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'street', kind: CompletionItemKind.Field },
{ label: 'number', kind: CompletionItemKind.Field },
{ label: 'alpha', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([email, address.|]) with composite type suggestion, depth 1', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
alpha Alpha
}
type Alpha {
bravo Bravo
helloA Int
}
type Bravo {
something String
helloBravo Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([email, address.|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'street', kind: CompletionItemKind.Field },
{ label: 'number', kind: CompletionItemKind.Field },
{ label: 'alpha', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([email, address.alpha.|]) with composite type suggestion, depth 2', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
alpha Alpha
}
type Alpha {
bravo Bravo
helloA Int
}
type Bravo {
something String
helloBravo Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([email, address.alpha.|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'bravo', kind: CompletionItemKind.Field },
{ label: 'helloA', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([email, address.alpha.bravo.|]) with composite type suggestion, depth 3', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
alpha Alpha
}
type Alpha {
bravo Bravo
helloA Int
}
type Bravo {
something String
helloBravo Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([email, address.alpha.bravo.|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'something', kind: CompletionItemKind.Field },
{ label: 'helloBravo', kind: CompletionItemKind.Field },
],
},
})
})
test('@@index([email, address.alpha.bravo.hello|]) with composite type suggestion, depth 3', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
alpha Alpha
}
type Alpha {
bravo Bravo
helloA Int
}
type Bravo {
something String
helloBravo Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index([email, address.alpha.bravo.hello||])
}`,
expected: {
isIncomplete: false,
// TODO, see if we can have better suggestions here, should suggest `helloBravo`
items: [],
},
})
})
test('@@index(fields: [|])', () => {
assertCompletion({
provider: 'mongodb',
schema: /* Prisma */ `
type Address {
street String
number Int
}
model User {
id Int @id @map("_id")
email String
address Address
@@index(fields: [|])
}`,
expected: {
isIncomplete: false,
items: [
{ label: 'id', kind: CompletionItemKind.Field },
{ label: 'email', kind: CompletionItemKind.Field },
{ label: 'address', kind: CompletionItemKind.Field },
],
},
})
})
})
describe('extendedIndexes - PostgreSQL', function () {
test('@@index(|)', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
model A {
id Int @id
title String
content String
@@index(|)
}
`,
expected: {
isIncomplete: false,
items: [fieldsProperty, mapProperty, typeProperty],
},
})
})
test('@@index([title], |) - postgresql', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
model A {
id Int @id
title String
content String
@@index([title], |)
}
`,
expected: {
isIncomplete: false,
items: [fieldsProperty, mapProperty, typeProperty],
},
})
})
test('@@index([title], type: |) - postgresql', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
model A {
id Int @id
title String
content String
@@index([title], type: |)
}
`,
expected: {
isIncomplete: false,
items: [
{ label: 'BTree', kind: CompletionItemKind.Enum },
{ label: 'Hash', kind: CompletionItemKind.Enum },
{ label: 'Gist', kind: CompletionItemKind.Enum },
{ label: 'Gin', kind: CompletionItemKind.Enum },
{ label: 'SpGist', kind: CompletionItemKind.Enum },
{ label: 'Brin', kind: CompletionItemKind.Enum },
],
},
})
})
test('@@index([title], type: Hash, |) - postgresql', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
model A {
id Int @id
title String
content String
@@index([title], type: Hash, |)
}
`,
expected: {
isIncomplete: false,
items: [fieldsProperty, mapProperty],
},
})
})
test('@@index([title(|)]) - postgresql', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
model A {
id Int @id
title String
content String
@@index([title(|)])
}
`,
expected: {
isIncomplete: false,
items: [
{ label: 'ops', kind: CompletionItemKind.Property },
{ label: 'sort', kind: CompletionItemKind.Property },
],
},
})
})
test('@@index([title(ops: |)]) - postgresql', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
model A {
id Int @id
title String @db.Inet
content String
@@index([title(ops: |)])
}
`,
expected: {
isIncomplete: false,
items: [{ label: 'raw', kind: CompletionItemKind.Function }],
},
})
})
test('@@index([title(ops: |)], type: Gist) - postgresql', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
model A {
id Int @id
title String @db.Inet
content String
@@index([title(ops: |)], type: Gist)
}
`,
expected: {
isIncomplete: false,
items: [
{ label: 'InetOps', kind: CompletionItemKind.Enum },
{ label: 'raw', kind: CompletionItemKind.Function },
],
},
})
})
})
})
describe('@@fulltext()', function () {
test('@@fulltext(|) - mysql', () => {
assertCompletion({
provider: 'mysql',
previewFeatures: ['fullTextIndex'],
schema: /* Prisma */ `
model Fulltext {
id Int @id
title String @db.VarChar(255)
content String @db.Text
@@fulltext(|)
@@fulltext([title, content], )
}
`,
expected: {
isIncomplete: false,
items: [fieldsProperty, mapProperty],
},
})
})
test('@@fulltext([title, content], |) - mysql', () => {
assertCompletion({
provider: 'mysql',
previewFeatures: ['fullTextIndex'],
schema: /* Prisma */ `
model Fulltext {
id Int @id
title String @db.VarChar(255)
content String @db.Text
@@fulltext()
@@fulltext([title, content], |)
}
`,
expected: {
isIncomplete: false,
items: [fieldsProperty, mapProperty],
},
})
})
test('@@fulltext(|) - mongodb', () => {
assertCompletion({
provider: 'mongodb',
previewFeatures: ['fullTextIndex'],
schema: /* Prisma */ `
model Fulltext {
id String @id @map("_id") @db.ObjectId
title String
content String
@@fulltext(|)
@@fulltext([title, content], )
}
// https://www.prisma.io/docs/concepts/components/prisma-schema/indexes#examples
// On MongoDB, the fullTextIndex and extendedIndexes preview features can be combined
// to add fields in ascending or descending order to your full-text index:
model Post {
id String @id @map("_id") @db.ObjectId
title String
content String
@@fulltext([title(sort: Desc), content])
}
`,
expected: {
isIncomplete: false,
items: [fieldsProperty, mapProperty],
},
})
})
test('@@fulltext([title, content], |) - mongodb', () => {
assertCompletion({
provider: 'mongodb',
previewFeatures: ['fullTextIndex'],
schema: /* Prisma */ `
model Fulltext {
id String @id @map("_id") @db.ObjectId
title String
content String
@@fulltext()
@@fulltext([title, content], |)
}
// https://www.prisma.io/docs/concepts/components/prisma-schema/indexes#examples
// On MongoDB, the fullTextIndex and extendedIndexes preview features can be combined
// to add fields in ascending or descending order to your full-text index:
model Post {
id String @id @map("_id") @db.ObjectId
title String
content String
@@fulltext([title(sort: Desc), content])
}
`,
expected: {
isIncomplete: false,
items: [fieldsProperty, mapProperty],
},
})
})
})
describe('@@schema()', () => {
test('@@schema - postgres', () => {
assertCompletion({
provider: 'postgresql',
previewFeatures: ['multiSchema'],
schema: /* prisma */ `
model Schema {
id Int @id
|
}
`,
expected: {
isIncomplete: false,
items: [
blockAttributeMap,
blockAttributeUnique,
blockAttributeIndex,
blockAttributeIgnore,
blockAttributeSchema,
],
},
})
})
test('@@schema(|) - postgres', () => {
assertCompletion({
schema: /* prisma */ `
generator client {
provider = "prisma-client-js"
previewFeatures = ["multiSchema"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
schemas = ["one", "two"]
}
model Schema {
id Int @id
@@schema(|)
}
`,
expected: {
isIncomplete: false,
items: [namespaceOne, namespaceTwo],
},
})
})
})
test('block suggestion should filter out block attributes that can only be defined once', () => {
assertCompletion({
schema: /* prisma */ `
generator client {
provider = "prisma-client-js"
previewFeatures = ["multiSchema", "fullTextIndex"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
schemas = ["one"]
}
model A {
id Int
name String
@@id([id])
@@unique([id])
@@index([id])
@@fulltext([name])
@@map("hi")
@@ignore
@@schema("bas")
|
}
`,
expected: {
isIncomplete: false,
items: [blockAttributeUnique, blockAttributeIndex, blockAttributeFulltextIndex],
},
})
})
})
describe('TYPES', () => {
describe('Views', () => {
test('Field Types', () => {
assertCompletion({
schema: /* Prisma */ `
view A {
name |
}
`,
expected: {
isIncomplete: true,
items: [
{ label: 'String', kind: CompletionItemKind.TypeParameter },
{ label: 'Boolean', kind: CompletionItemKind.TypeParameter },
{ label: 'Int', kind: CompletionItemKind.TypeParameter },
{ label: 'Float', kind: CompletionItemKind.TypeParameter },
{ label: 'DateTime', kind: CompletionItemKind.TypeParameter },
{ label: 'Json', kind: CompletionItemKind.TypeParameter },
{ label: 'Bytes', kind: CompletionItemKind.TypeParameter },
{ label: 'Decimal', kind: CompletionItemKind.TypeParameter },
{ label: 'BigInt', kind: CompletionItemKind.TypeParameter },
{
label: 'Unsupported',
kind: CompletionItemKind.TypeParameter,
},
{ label: 'A', kind: CompletionItemKind.Reference },
],
},
})
})
test('Field Attributes', () => {
assertCompletion({
provider: 'postgresql',
schema: /* Prisma */ `
view A {
name String |
}
`,
expected: {
isIncomplete: false,
items: [
{ label: '@db', kind: CompletionItemKind.Property },
{ label: '@id', kind: CompletionItemKind.Property },
{ label: '@unique', kind: CompletionItemKind.Property },
{ label: '@map', kind: CompletionItemKind.Property },
{ label: '@default', kind: CompletionItemKind.Property },
{ label: '@relation', kind: CompletionItemKind.Property },
{ label: '@ignore', kind: CompletionItemKind.Property },