mirror of
https://github.com/JetBrains/intellij-sdk-code-samples.git
synced 2025-07-30 18:27:49 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
ce6d8b91d8
@ -163,7 +163,7 @@
|
||||
|
||||
## Part VI - Testing
|
||||
|
||||
* [Testing Plugins](basics/testing_plugins.md)
|
||||
* [Testing Plugins](basics/testing_plugins/testing_plugins.md)
|
||||
* [Tests and Fixtures](basics/testing_plugins/tests_and_fixtures.md)
|
||||
* [Light and Heavy Tests](basics/testing_plugins/light_and_heavy_tests.md)
|
||||
* [Test Project and Testdata Directories](basics/testing_plugins/test_project_and_testdata_directories.md)
|
||||
|
@ -2,40 +2,36 @@
|
||||
title: File View Providers
|
||||
---
|
||||
|
||||
A file view provider (see the [`FileViewProvider`](upsource:///platform/core-api/src/com/intellij/psi/FileViewProvider.java) class) was introduced in *IntelliJ IDEA* 6.0. Its main purpose is to manage access to multiple PSI trees within a single file.
|
||||
A file view provider ([`FileViewProvider`](upsource:///platform/core-api/src/com/intellij/psi/FileViewProvider.java)) manages access to multiple PSI trees within a single file.
|
||||
|
||||
For example, a JSPX page has a separate PSI tree for the Java code in it (`PsiJavaFile`), a separate tree for the XML code (`XmlFile`), and a separate tree for JSP as a whole [`JspFile`](upsource:///java/jsp-openapi/src/com/intellij/psi/jsp/JspFile.java)).
|
||||
|
||||
Each of the PSI trees covers the entire contents of the file, and contains special "outer language elements" in the places where contents in a different language can be found.
|
||||
|
||||
A [`FileViewProvider`](upsource:///platform/core-api/src/com/intellij/psi/FileViewProvider.java) instance corresponds to a single `VirtualFile`, a single `Document`, and can be used to retrieve multiple `PsiFile` instances.
|
||||
A `FileViewProvider` instance corresponds to a single `VirtualFile`, a single `Document`, and can be used to retrieve multiple `PsiFile` instances.
|
||||
|
||||
## How do I get an FVP?
|
||||
## How do I get a FileViewProvider?
|
||||
|
||||
* From a VirtualFile: `PsiManager.getInstance(project).findViewProvider()`
|
||||
* From a PSI file: `psiFile.getViewProvider()`
|
||||
* From a `VirtualFile`: `PsiManager.getInstance(project).findViewProvider()`
|
||||
* From a `PsiFile`: `psiFile.getViewProvider()`
|
||||
|
||||
## What can I do with an FVP?
|
||||
## What can I do with a FileViewProvider?
|
||||
|
||||
* To get the set of all languages for which PSI trees exist in a file: `fileViewProvider.getLanguages()`
|
||||
* To get the PSI tree for a particular language: `fileViewProvider.getPsi(language)`, where the `language` parameter can take values of the [`Language`](upsource:///platform/core-api/src/com/intellij/lang/Language.java) type defined in [StdLanguages](upsource:///platform/platform-api/src/com/intellij/lang/StdLanguages.java) class. For example, to get the PSI tree for XML, use `fileViewProvider.getPsi(StdLanguages.XML)`.
|
||||
* To get the PSI tree for a particular language: `fileViewProvider.getPsi(language)`. For example, to get the PSI tree for XML, use `fileViewProvider.getPsi(XMLLanguage.INSTANCE)`.
|
||||
* To find an element of a particular language at the specified offset in the file: `fileViewProvider.findElementAt(offset, language)`
|
||||
|
||||
## How do I extend the FileViewProvider?
|
||||
|
||||
To create a file type that has multiple interspersing trees for different languages, your plugin must contain an extension to the `fileType.fileViewProviderFactory` [extension point](/basics/plugin_structure/plugin_extensions_and_extension_points.md) available in the *IntelliJ Platform* core.
|
||||
To create a file type that has multiple interspersing trees for different languages, a plugin must contain an extension to the `com.intellij.fileType.fileViewProviderFactory` extension point.
|
||||
|
||||
This extension point is declared using the [`FileTypeExtensionPoint`](upsource:///platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeExtensionPoint.java)
|
||||
bean class.
|
||||
|
||||
To access this extension point, create a Java class that implements the [`FileViewProviderFactory`](upsource:///platform/core-api/src/com/intellij/psi/FileViewProviderFactory.java) interface, and in this class, override the `createFileViewProvider` method.
|
||||
|
||||
To declare the extension to the `fileType.fileViewProviderFactory` extension point, add the following syntax to the `<extensions>` section of the `plugin.xml` file:
|
||||
Implement [`FileViewProviderFactory`](upsource:///platform/core-api/src/com/intellij/psi/FileViewProviderFactory.java) and return your `FileViewProvider` implementation from `createFileViewProvider()` method.
|
||||
|
||||
Register as follows in `plugin.xml`:
|
||||
```xml
|
||||
<extensions>
|
||||
<fileType.fileViewProviderFactory filetype="%file_type%" implementationClass="%class_name%" />
|
||||
<fileType.fileViewProviderFactory filetype="%file_type%" implementationClass="com.plugin.MyFileViewProviderFactory" />
|
||||
</extensions>
|
||||
```
|
||||
|
||||
Where `%file_type%` refers to the type of the file being created (for example, "JFS"), and the `%class_name%` refers to the name of your Java class that implements the [`FileViewProviderFactory`](upsource:///platform/core-api/src/com/intellij/psi/FileViewProviderFactory.java) interface.
|
||||
Where `%file_type%` refers to the type of the file being created (for example, "JFS").
|
@ -5,9 +5,9 @@ title: Navigating the PSI
|
||||
There are three main ways to navigate the PSI: *top-down*, *bottom-up*, and using *references*. In the first scenario,
|
||||
you have a PSI file or another higher-level element (for example, a method), and you need to find all elements that match a
|
||||
specified condition (for example, all variable declarations). In the second scenario, you have a specific point
|
||||
in the PSI tree (for example, the element at caret), and need to find out something about its context (for example,
|
||||
in the PSI tree (for example, the element at caret) and need to find out something about its context (for example,
|
||||
the element in which it has been declared). Finally, *references* allow you to navigate from the use of an element
|
||||
(e.g. a method call) to the declaration (the method being called) and back. References are described in a
|
||||
(e.g., a method call) to the declaration (the method being called) and back. References are described in a
|
||||
[separate topic](psi_references.md).
|
||||
|
||||
|
||||
|
@ -31,4 +31,4 @@ Sometimes, the following conditions hold:
|
||||
* eagerly calculating the data for the entire project during indexing isn't needed (e.g. it slows down the indexing, and/or this data probably will ever be needed for a minor subset of all project files)
|
||||
* the data can be recalculated lazily on request without major performance penalties
|
||||
|
||||
In such cases, file-based index can be used, but file gists provide a way to perform data calculation lazily, caching on disk, and a more lightweight API. Please see [`VirtualFileGist`](upsource:///platform/indexing-api/src/com/intellij/util/gist/VirtualFileGist.java) and [PsiFileGist](upsource:///platform/indexing-api/src/com/intellij/util/gist/PsiFileGist.java) documentation.
|
||||
In such cases, file-based index can be used, but file gists provide a way to perform data calculation lazily, caching on disk, and a more lightweight API. Please see [`VirtualFileGist`](upsource:///platform/indexing-api/src/com/intellij/util/gist/VirtualFileGist.java) and [`PsiFileGist`](upsource:///platform/indexing-api/src/com/intellij/util/gist/PsiFileGist.java) documentation.
|
||||
|
@ -36,7 +36,7 @@ An implementation of a file-based index consists of the following main parts:
|
||||
|
||||
If you don't need to associate any value with the files (i.e. your value type is Void), you can simplify the implementation by using [`ScalarIndexExtension`](upsource:///platform/indexing-impl/src/com/intellij/util/indexing/ScalarIndexExtension.java) as the base class.
|
||||
|
||||
> **Note** The data returned by `DataIndexer.map()` must depend only on input data passed to the method, and must not depend on any external files. Otherwise your index will not be correctly updated when the external data changes, and you will have stale data in your index.
|
||||
> **WARNING** The data returned by `DataIndexer.map()` must depend only on input data passed to the method, and must not depend on any external files. Otherwise your index will not be correctly updated when the external data changes, and you will have stale data in your index.
|
||||
|
||||
> **Note** Please see `com.intellij.util.indexing.DebugAssertions` on how to enable additional debugging assertions during development to assert correct index implementation.
|
||||
|
||||
@ -54,7 +54,7 @@ The following primary operations are supported:
|
||||
* `getContainingFiles()` allows to obtain all files in which a specific key was encountered.
|
||||
* `processValues()` allows to iterate though all files in which a specific key was encountered and to access the associated values at the same time.
|
||||
|
||||
> **Note** Nested index access is forbidden as it might lead to a deadlock. Collect all necessary data from index A first, then process results while accessing index B.
|
||||
> **WARNING** Nested index access is forbidden as it might lead to a deadlock. Collect all necessary data from index A first, then process results while accessing index B.
|
||||
|
||||
## Standard indexes
|
||||
|
||||
|
@ -41,7 +41,7 @@ It's essential to make sure that all information stored in the stub tree depends
|
||||
|
||||
When building the stub tree, you can at the same time put some data about the stub elements into a number of indexes, which then can be used to find the PSI elements by the corresponding key. Unlike file-based indexes, stub indexes do not support storing custom data as values; the value is always a PSI element. Keys in stub indexes are normally strings (such as class names); other data types are also supported if desired.
|
||||
|
||||
A stub index is a class which extends [`AbstractStubIndex`](upsource:///platform/indexing-api/src/com/intellij/psi/stubs/AbstractStubIndex.java). In the most common case, when the key type is `String`, you use a more specific base class, namely [StringStubIndexExtension](upsource:///platform/indexing-api/src/com/intellij/psi/stubs/StringStubIndexExtension.java). Stub index implementation classes are registered in the `<stubIndex>` extension point.
|
||||
A stub index is a class which extends [`AbstractStubIndex`](upsource:///platform/indexing-api/src/com/intellij/psi/stubs/AbstractStubIndex.java). In the most common case, when the key type is `String`, you use a more specific base class, namely [`StringStubIndexExtension`](upsource:///platform/indexing-api/src/com/intellij/psi/stubs/StringStubIndexExtension.java). Stub index implementation classes are registered in the `com.intellij.stubIndex` extension point.
|
||||
|
||||
To put data into an index, you implement the method `IStubElementType.indexStub()` ([example: `JavaClassElementType.indexStub()`](upsource:///java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassElementType.java)<!--#L189-->). This method accepts an `IndexSink` as a parameter, and puts in the index ID and the key for each index in which the element should be stored.
|
||||
|
||||
|
@ -4,19 +4,19 @@ title: PSI Cookbook
|
||||
|
||||
This page gives a list of recipes for the most common operations for working with the PSI (Program Structure Interface). Unlike [Developing Custom Language Plugins](/reference_guide/custom_language_support.md), it talks about working with the PSI of existing languages (such as Java).
|
||||
|
||||
## How do I find a file if I know its name but don't know the path?
|
||||
### How do I find a file if I know its name but don't know the path?
|
||||
|
||||
`FilenameIndex.getFilesByName()`
|
||||
|
||||
## How do I find where a particular PSI element is used?
|
||||
### How do I find where a particular PSI element is used?
|
||||
|
||||
`ReferencesSearch.search()`
|
||||
|
||||
## How do I rename a PSI element?
|
||||
### How do I rename a PSI element?
|
||||
|
||||
`RefactoringFactory.createRename()`
|
||||
|
||||
## How can I cause the PSI for a virtual file to be rebuilt?
|
||||
### How can I cause the PSI for a virtual file to be rebuilt?
|
||||
|
||||
`FileContentUtil.reparseFiles()`
|
||||
|
||||
|
@ -7,9 +7,9 @@ The test fixture creates a *test project* environment. Unless you customize the
|
||||
[`LightPlatformCodeInsightFixtureTestCase`](upsource:///platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java) (renamed to `BasePlatformTestCase` in 2019.2) uses an in-memory implementation; if you set up the test environment by calling `IdeaTestFixtureFactory.createCodeInsightFixture`, you can specify the implementation to use.
|
||||
|
||||
> **WARNING** If your tests use the in-memory implementation, and you abort the execution of your tests, the persisted filesystem caches may get out of sync with the in-memory structures, and you may get spurious errors in your tests.
|
||||
> If you get an unexpected error after a series of successful runs, **try running the test again**, and if that doesn't help, **delete the "system" subdirectory** in your sandbox directory (specified via `sandboxDirectory` for Gradle setups or in the *IntelliJ Platform* SDK settings for Devkit setups) .
|
||||
> If you get an unexpected error after a series of successful runs, **try rerunning the test**, and if that doesn't help, **delete the "system" subdirectory** in your [sandbox directory](/basics/ide_development_instance.md#the-development-instance-sandbox-directory).
|
||||
|
||||
In your plugin, you normally store the test data for your tests (such as files on which plugin features will be executed and expected output files) in the `testdata` directory. This is just a directory under the content root of your plugin, but not under a source root. Files in `testdata` are normally not valid source code and must not be compiled.
|
||||
In your plugin, you normally store the test data for your tests (such as files on which plugin features will be executed and expected output files) in the `testdata` directory. This is just a directory under the content root of your plugin, but not under a source root. Files in `testdata` usually are not valid source code and must not be compiled.
|
||||
|
||||
To specify the location of `testdata`, you must override the `getTestDataPath()` method. The default implementation assumes running as part of the *IntelliJ Platform* source tree and is not appropriate for third-party plugins.
|
||||
|
||||
@ -23,8 +23,10 @@ Most operations in plugin tests require a file open in the in-memory editor, in
|
||||
|
||||
Alternatively, you can use one of the other methods which take parameters annotated with [`@TestDataFile`](upsource:///platform/testFramework/src/com/intellij/testFramework/TestDataFile.java). These methods copy the specified files from the `testdata` directory to the test project directory, open the first of the specified files in the in-memory editor, and then perform the requested operation such as highlighting or code completion.
|
||||
|
||||
When a file is opened in the in-memory editor, special markup in the file content can be used to specify the caret position or selection. You can use one of the following markers:
|
||||
### Special Markup
|
||||
When a file is opened in the in-memory editor, special markup in the file content can be used to specify the caret position or selection.
|
||||
|
||||
You can use one of the following markers:
|
||||
* `<caret>` specifies the position where the caret should be placed.
|
||||
* `<selection>` and `</selection>` specify the start and end of the text range to be selected.
|
||||
* `<block>` and `</block>` specify the start and end points of the column selection.
|
||||
|
@ -34,9 +34,9 @@ The tag name specifies the severity of the expected highlighting. The following
|
||||
|
||||
The tag can also have the following optional attributes:
|
||||
|
||||
* `descr` - expected message associated with the highlighter (if not specified, any text will match; if the message contains a quotation mark, it can be escaped by putting two backslash characters before it)
|
||||
* `descr` expected message associated with the highlighter (if not specified, any text will match; if the message contains a quotation mark, it can be escaped by putting two backslash characters before it)
|
||||
* `foregroundColor`, `backgroundColor`, `effectColor` expected colors for the highlighting
|
||||
* `effectType` expected effect type for the highlighting (see `EffectType` enum for possible values)
|
||||
* `effectType` expected effect type for the highlighting (see [`EffectType`](upsource:///platform/core-api/src/com/intellij/openapi/editor/markup/EffectType.java))
|
||||
* `fontType` expected font style for the highlighting (0 - normal, 1 - bold, 2 - italic, 3 - bold italic)
|
||||
|
||||
> **Note** *Nested* tags are **supported**:
|
||||
|
@ -1,10 +1,12 @@
|
||||
---
|
||||
title: Testing Plugins
|
||||
redirect_from:
|
||||
- /basics/testing_plugins.html
|
||||
---
|
||||
|
||||
Most of the tests in the *IntelliJ Platform* codebase are *model level functional tests*. What this means is the following:
|
||||
|
||||
* The tests run in a headless environment which uses real production implementations for the majority of components, except for a number of UI components.
|
||||
* The tests run in a headless environment that uses real production implementations for the majority of components, except for a number of UI components.
|
||||
* The tests usually test a feature as a whole, rather than individual functions that comprise its implementation.
|
||||
* The tests do not test the Swing UI and work directly with the underlying model instead.
|
||||
* Most of the tests take a source file or a set of source files as input data, execute a feature, and then compare the output with expected results. Results can be specified as another set of source files, as special markup in the input file, or directly in the test code.
|
||||
@ -18,10 +20,11 @@ Another consequence of our testing approach is what our test framework does not
|
||||
* We do not provide a recommended approach to mocking. We have a few tests in our codebase that use JMock, but in general, we find it difficult to mock all of the interactions with *IntelliJ Platform* components that your plugin class will need to have, and we recommend working with real components instead.
|
||||
* We do not provide a general-purpose framework for Swing UI testing. You can try using tools such as [FEST](https://code.google.com/p/fest/) or [Sikuli](http://sikulix.com/) for plugin UI testing, but we don't use either of them and cannot provide any guidelines for their use. Internally, we use manual testing for testing our Swing UIs.
|
||||
|
||||
* [Tests and Fixtures](/basics/testing_plugins/tests_and_fixtures.md)
|
||||
* [Light and Heavy Tests](/basics/testing_plugins/light_and_heavy_tests.md)
|
||||
* [Test Project and Testdata Directories](/basics/testing_plugins/test_project_and_testdata_directories.md)
|
||||
* [Writing Tests](/basics/testing_plugins/writing_tests.md)
|
||||
* [Testing Highlighting](/basics/testing_plugins/testing_highlighting.md)
|
||||
## Topics
|
||||
* [Tests and Fixtures](tests_and_fixtures.md)
|
||||
* [Light and Heavy Tests](light_and_heavy_tests.md)
|
||||
* [Test Project and Testdata Directories](test_project_and_testdata_directories.md)
|
||||
* [Writing Tests](writing_tests.md)
|
||||
* [Testing Highlighting](testing_highlighting.md)
|
||||
|
||||
Check out [this step-by-step tutorial](/tutorials/writing_tests_for_plugins.md) teaching how to write and run automated tests for your custom language plugin (source code included).
|
@ -4,8 +4,8 @@ title: Tests and Fixtures
|
||||
|
||||
The *IntelliJ Platform* testing infrastructure is not tied to any specific test framework. In fact, the IntelliJ IDEA Team uses JUnit, TestNG and Cucumber for testing different parts of the project. However, most of the tests are written using JUnit 3.
|
||||
|
||||
When writing your own tests, you have the choice between using a standard base class to perform the test set up for you and using a fixture class, which lets you perform the setup manually and does not tie you to a specific test framework.
|
||||
When writing your tests, you have the choice between using a standard base class to perform the test set up for you and using a fixture class, which lets you perform the setup manually and does not tie you to a specific test framework.
|
||||
|
||||
With the former approach, you can use classes such as [`LightPlatformCodeInsightFixtureTestCase`](upsource:///platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java) (renamed to `BasePlatformTestCase` in 2019.2).
|
||||
With the former approach, you can use classes such as [`LightPlatformCodeInsightFixtureTestCase`](upsource:///platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java) (renamed to [`BasePlatformTestCase`](upsource:///platform/testFramework/src/com/intellij/testFramework/fixtures/BasePlatformTestCase.java) in 2019.2).
|
||||
|
||||
With the latter approach, you use the [`IdeaTestFixtureFactory`](upsource:///platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaTestFixtureFactory.java) class to create instances of fixtures for the test environment, and you need to call the fixture creation and setup methods from the test setup method used by your test framework.
|
||||
|
@ -2,8 +2,9 @@
|
||||
title: Writing Tests
|
||||
---
|
||||
|
||||
In most cases, once you have the necessary files copied to the test project and loaded into the in-memory editor, writing the test itself involves invoking your plugin code and has few dependencies on the test framework. However, for many common cases the framework provides helper methods that can make testing easier:
|
||||
In most cases, once you have the necessary files copied to the test project and loaded into the in-memory editor, writing the test itself involves invoking your plugin code and has few dependencies on the test framework.
|
||||
|
||||
However, for many common cases, the framework provides helper methods that can make testing easier:
|
||||
* `type()` simulates the typing of a character or string into the in-memory editor.
|
||||
* `performEditorAction()` simulates the execution of an action in the context of the in-memory editor.
|
||||
* `complete()` simulates the invocation of code completion and returns the list of lookup elements displayed in the completion list (or `null` if the completion had no suggestions or one suggestion which was auto-inserted).
|
||||
@ -11,4 +12,4 @@ In most cases, once you have the necessary files copied to the test project and
|
||||
* `findSingleIntention()` in combination with `launchAction()` simulate the invocation of an intention action or inspection quickfix with the specified name.
|
||||
* `renameElementAtCaret()` or `rename()` simulate the execution of a rename refactoring.
|
||||
|
||||
To compare the results of executing the action with the expected results, you can use the `checkResultByFile()` method. The file with the expected results can also contain markup to specify the expected caret position or selected text range. If you're testing an action which modifies multiple files (a project-wide refactoring, for example), you can compare an entire directory under the test project with the expected output using `PlatformTestUtil.assertDirectoriesEqual()`.
|
||||
To compare the results of executing the action with the expected results, you can use the `checkResultByFile()` method. The file with the expected results can also contain [markup](test_project_and_testdata_directories.md#special-markup) to specify the expected caret position or selected text range. If you're testing an action that modifies multiple files (a project-wide refactoring, for example), you can compare an entire directory under the test project with the expected output using `PlatformTestUtil.assertDirectoriesEqual()`.
|
||||
|
@ -28,7 +28,7 @@ The Program Structure Interface builds the syntactic and semantic models for lot
|
||||
|
||||
Describes how to extend and interact with various features that use the PSI layer, such as code completion, navigation, <kbd>Alt</kbd>+<kbd>Enter</kbd> items, intentions, refactorings and more. See also the section on Custom Languages below for language specific features that are only applicable when adding support for a new language.
|
||||
|
||||
#### [Part VI - Testing](/basics/testing_plugins.md)
|
||||
#### [Part VI - Testing](/basics/testing_plugins/testing_plugins.md)
|
||||
|
||||
Describes the available infrastructure for writing automated tests covering the functionality of plugins.
|
||||
|
||||
|
@ -7,7 +7,7 @@ The _IntelliJ Platform_ is very large, and very capable, and its size and scope
|
||||
## Essential concepts
|
||||
|
||||
- [Getting Started](/basics/getting_started.md) with plugins.
|
||||
- [Testing plugins](/basics/testing_plugins.md).
|
||||
- [Testing plugins](/basics/testing_plugins/testing_plugins.md).
|
||||
- Component model - the _IntelliJ Platform_ is a component based application, and is responsible for creating components and injecting dependencies. Understanding this is necessary for building plugins.
|
||||
- [Extension points](/basics/plugin_structure/plugin_extensions_and_extension_points.md) - how to register components with extension points, and how to find out what extension points are available.
|
||||
- [Virtual files](/basics/architectural_overview/virtual_file.md) - all file access should go through the Virtual File System which abstracts and caches the file system. This means you can work with files that are on the local file system, in a zip file or are old versions from version control.
|
||||
|
@ -126,7 +126,7 @@ Implicit in using [`LocalInspectionTool`](upsource:///platform/analysis-api/src/
|
||||
|
||||
### Inspection Unit Test
|
||||
The `comparing_references_inspection` code sample provides a unit test for the inspection.
|
||||
See the [Testing Plugins](/basics/testing_plugins.md) section for general information about plugin testing.
|
||||
See the [Testing Plugins](/basics/testing_plugins/testing_plugins.md) section for general information about plugin testing.
|
||||
|
||||
The `comparing_references_inspection` test is based on the [`UsefulTestCase`](upsource:///platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java) class, part of the JUnit framework APIs.
|
||||
This class handles much of the underlying boilerplate for tests.
|
||||
|
@ -2,7 +2,7 @@
|
||||
title: Writing Tests For Plugins
|
||||
---
|
||||
|
||||
> **NOTE** Please see [Testing Plugins](/basics/testing_plugins.md) for a general introduction.
|
||||
> **NOTE** Please see [Testing Plugins](/basics/testing_plugins/testing_plugins.md) for a general introduction.
|
||||
|
||||
In this tutorial you will learn how to write and run automated tests for a custom language plugin.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user