// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.jetbrains.utils.rules import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterManager import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterRef import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreter import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreterManager import com.intellij.lang.javascript.dialects.JSLanguageLevel import com.intellij.lang.javascript.psi.JSFile import com.intellij.lang.javascript.settings.JSRootConfiguration import com.intellij.openapi.module.WebModuleTypeBase import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.util.Ref import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.testFramework.LightProjectDescriptor import com.intellij.testFramework.PsiTestUtil import com.intellij.testFramework.fixtures.CodeInsightTestFixture import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory import com.intellij.testFramework.runInEdtAndGet import com.intellij.testFramework.runInEdtAndWait import com.intellij.util.text.SemVer import com.intellij.xdebugger.XDebuggerUtil import org.intellij.lang.annotations.Language /** * JUnit test Rule that will create a Light [Project] and [CodeInsightTestFixture] with NodeJs support. Projects are * lazily created and are torn down after each test. * * If you wish to have just a [Project], you may use Intellij's [com.intellij.testFramework.ProjectRule] */ class NodeJsCodeInsightTestFixtureRule : CodeInsightTestFixtureRule(NodeJsLightProjectDescriptor()) { override fun createTestFixture(): CodeInsightTestFixture { val codeInsightFixture = super.createTestFixture() PsiTestUtil.addContentRoot(codeInsightFixture.module, codeInsightFixture.tempDirFixture.getFile(".")!!) codeInsightFixture.project.setNodeJsInterpreterVersion(SemVer("v8.10.10", 8, 10, 10)) codeInsightFixture.project.setJsLanguageLevel(JSLanguageLevel.ES6) return codeInsightFixture } fun addBreakpoint() { runInEdtAndWait { val document = fixture.editor.document val psiFile = fixture.file as JSFile val lineNumber = document.getLineNumber(psiFile.statements.first().textOffset) XDebuggerUtil.getInstance().toggleLineBreakpoint( project, fixture.file.virtualFile, lineNumber ) } } } class NodeJsLightProjectDescriptor : LightProjectDescriptor() { override fun getSdk(): Sdk? = null override fun getModuleTypeId(): String = WebModuleTypeBase.getInstance().id } class MockNodeJsInterpreter(private var version: SemVer) : NodeJsLocalInterpreter("/path/to/$version/mock/node") { init { NodeJsLocalInterpreterManager.getInstance().interpreters = NodeJsLocalInterpreterManager.getInstance().interpreters + listOf(this) } // could differ on windows causing interpreter lookup failure during tests override fun getPresentableName(): String = referenceName override fun getCachedVersion(): Ref<SemVer> = Ref(version) } class HeavyNodeJsCodeInsightTestFixtureRule : CodeInsightTestFixtureRule() { override fun createTestFixture(): CodeInsightTestFixture { val fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory() val projectFixture = fixtureFactory.createFixtureBuilder(testName) val codeInsightFixture = fixtureFactory.createCodeInsightFixture(projectFixture.fixture) codeInsightFixture.setUp() codeInsightFixture.testDataPath = testDataPath return codeInsightFixture } fun addBreakpoint() { runInEdtAndWait { val document = fixture.editor.document val psiFile = fixture.file as JSFile val lineNumber = document.getLineNumber(psiFile.statements.first().textOffset) XDebuggerUtil.getInstance().toggleLineBreakpoint( project, fixture.file.virtualFile, lineNumber ) } } } fun Project.setNodeJsInterpreterVersion(version: SemVer) { NodeJsInterpreterManager.getInstance(this).setInterpreterRef( NodeJsInterpreterRef.create(MockNodeJsInterpreter(version)) ) } fun Project.setJsLanguageLevel(languageLevel: JSLanguageLevel) { JSRootConfiguration.getInstance(this) .storeLanguageLevelAndUpdateCaches(languageLevel) } fun CodeInsightTestFixture.addLambdaHandler( subPath: String = ".", fileName: String = "app", handlerName: String = "lambdaHandler", @Language("JS") fileContent: String = """ exports.$handlerName = function (event, context, callback) { return 'HelloWorld' }; """.trimIndent() ): PsiElement { val psiFile = this.addFileToProject("$subPath/$fileName.js", fileContent) as JSFile return runInEdtAndGet { psiFile.findElementAt(fileContent.indexOf(handlerName))!! } } fun CodeInsightTestFixture.addTypeScriptLambdaHandler( subPath: String = ".", fileName: String = "app", handlerName: String = "lambdaHandler", @Language("TS") fileContent: String = """ export const $handlerName = (event: APIGatewayProxyEvent, context: Context, callback: Callback<APIGatewayProxyResult>): APIGatewayProxyResult => { return { statusCode: 200 } } """.trimIndent() ): PsiElement { val psiFile = this.addFileToProject("$subPath/$fileName.ts", fileContent) as JSFile return runInEdtAndGet { psiFile.findElementAt(fileContent.indexOf(handlerName))!! } } fun CodeInsightTestFixture.addPackageJsonFile( subPath: String = ".", @Language("JSON") content: String = """ { "name": "hello-world", "version": "1.0.0" } """.trimIndent() ): PsiFile = this.addFileToProject("$subPath/package.json", content) fun CodeInsightTestFixture.addTypeScriptPackageJsonFile( subPath: String = ".", @Language("JSON") content: String = """ { "name": "hello-world", "version": "1.0.0", "devDependencies": { "typescript": "*" } } """.trimIndent() ): PsiFile = this.addPackageJsonFile(subPath, content)