UNPKG

@prisma/language-server

Version:
1,742 lines (1,669 loc) • 113 kB
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 },