├── .gitignore
├── LICENSE.md
├── README.md
├── doc
├── create_test_dialog.png
├── default_run_configuration.png
├── preferences.png
├── run_configuration.png
└── test_results.png
├── intellij-nette-tester.iml
├── intellij-nette-tester.jar
├── resources
├── META-INF
│ ├── plugin.xml
│ └── pluginIcon.svg
├── fileTemplates
│ └── internal
│ │ └── Tester TestCase.ft
├── icons
│ ├── run.png
│ ├── runClass.png
│ ├── runMethod.png
│ └── testConfig.png
├── inspectionDescriptions
│ ├── TestCaseAnnotation.html
│ ├── TestCaseIsRun.html
│ └── TestFileName.html
├── messages
│ └── TesterBundle.properties
├── setup.php
└── setup2-0.php
└── src
└── cz
└── jiripudil
└── intellij
└── nette
└── tester
├── TesterBundle.java
├── TesterIcons.java
├── TesterPostStartupActivity.java
├── TesterTestFinder.java
├── TesterUtil.java
├── action
├── BaseTesterRunAction.java
├── TesterCreateMethodRunTestAction.java
├── TesterCreateRunTestAction.java
├── TesterMethodRunTestAction.java
└── TesterRunTestAction.java
├── codeGeneration
├── TesterAbstractGenerateMethodAction.java
├── TesterGenerateSetupMethodAction.java
├── TesterGenerateTeardownMethodAction.java
├── TesterGenerateTestMethodAction.java
├── TesterNamespaceMapper.java
├── TesterNewTestCaseAction.java
├── TesterNewTestCaseDialog.form
├── TesterNewTestCaseDialog.java
└── TesterTestCreator.java
├── configuration
├── TesterRunConfiguration.java
├── TesterRunConfigurationProducer.java
├── TesterRunConfigurationType.java
├── TesterSettings.java
├── TesterTestMethodRunConfiguration.java
├── TesterTestMethodRunConfigurationProducer.java
├── TesterTestMethodRunConfigurationType.java
└── editor
│ ├── PhpCommandLineSettingsEditor.form
│ ├── PhpCommandLineSettingsEditor.java
│ ├── TesterRunConfigurationEditor.form
│ ├── TesterRunConfigurationEditor.java
│ ├── TesterSettingsEditor.form
│ ├── TesterSettingsEditor.java
│ ├── TesterTestEnvironmentSettingsEditor.form
│ └── TesterTestEnvironmentSettingsEditor.java
├── execution
├── TesterConsoleProperties.java
├── TesterExecutionUtil.java
├── TesterStackTraceFilter.java
└── TesterTestLocator.java
├── inspections
├── TestCaseAnnotationInspection.java
├── TestCaseIsRunInspection.java
└── TestFileNameInspection.java
├── lineMarker
├── TesterMethodRunLineMarkerProvider.java
└── TesterRunLineMarkerProvider.java
└── projectSettings
├── TesterConfigurable.java
├── TesterNamespaceMapping.java
├── TesterProjectSettings.java
├── TesterProjectSettingsManager.java
└── editor
├── NamespaceMappingTable.java
├── NamespaceTableCellEditor.form
├── NamespaceTableCellEditor.java
├── TesterConfigurableForm.form
└── TesterConfigurableForm.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | out/
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, Jiří Pudil
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 |
8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 |
10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nette Tester integration into PhpStorm
2 |
3 | [](https://plugins.jetbrains.com/plugin/8226)
4 | [](https://plugins.jetbrains.com/plugin/8226)
5 |
6 | This plugin integrates [Nette Tester](https://tester.nette.org) into PhpStorm IDE.
7 |
8 |
9 | ## Installation and requirements
10 |
11 | This plugin is written for PhpStorm 2016.3 and above and is compiled for Java 8. You can find it in the Jetbrains plugin repository. Install it from Preferences → Plugins → Browse repositories... and search for `Tester`.
12 |
13 |
14 | ## Usage
15 |
16 | ### Configuration
17 |
18 | This plugin provides a new configuration type for Nette Tester:
19 |
20 | 
21 |
22 | - **Test scope** is the directory containing the tests you wish to run.
23 | - **Tester executable** specifies path to the Tester runner (`/path/to/your/project/vendor/bin/tester` if you installed Tester via Composer).
24 | - **Tester options** allows you to specify options for Tester script (refer to the [docs](https://tester.nette.org/en/)). This field serves for options you cannot specify otherwise (see below).
25 | - **Setup script** allows you to specify the setup script (`--setup` option). You can leave this field blank if you don't use any.
26 | - **Interpreter** and **interpreter options** allow you to modify the environment in which your tests run (`-p` option and `-d` options).
27 | - **Path to php.ini** specifies the configuration file to use (`-c` option). You can leave this field blank, in which case tests will run without any configuration loaded.
28 |
29 | #### Usage on Windows
30 |
31 | Composer seems to do some necessary, but unfortunate transformations on vendor binaries. Therefore you need to point the **Tester executable** option to the actual PHP file in `/path/to/your/project/vendor/nette/tester/src/tester.php`.
32 |
33 |
34 | ### Interpreting results
35 |
36 | If you now run this configuration, test results will start to show in the Test Runner window:
37 |
38 | 
39 |
40 | To the left, there is a list of tests. You can toggle showing passed and skipped tests. If you click the test, you will see the detailed output in the console window to the right.
41 |
42 |
43 | ### Navigating between a class and its test
44 |
45 | You can navigate between a class and its test, or create a test for a class easily, via Navigate → Test.
46 |
47 | The navigation is based on convention (class name + `Test` suffix), the creation assumes your tests reside in the same namespace as the code; if you use a different scheme, you can configure source to test namespace mapping in the project settings under Languages & Frameworks → PHP → Nette Tester.
48 |
49 | 
50 |
51 | If you create a test for a class, the dialog now follows your namespace mapping and automatically updates the target test namespace (and directory, following the Directories project settings).
52 |
53 | 
54 |
55 | In the project settings, you can also configure the path to your test environment bootstrap file, which is then automatically required in the generated TestCase files.
56 |
57 |
58 | ### Running a single test method
59 |
60 | You can right-click a single test method to run or even debug it in isolation. This merely executes the file as a PHP script, with the method name as a single argument.
61 |
62 |
63 | ### Running tests from a directory
64 |
65 | You can right-click a directory to run the tests within it. For this to be truly a one-click action, you should configure the Tester executable in the Nette Tester default run configuration template.
66 |
67 | 
68 |
--------------------------------------------------------------------------------
/doc/create_test_dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/doc/create_test_dialog.png
--------------------------------------------------------------------------------
/doc/default_run_configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/doc/default_run_configuration.png
--------------------------------------------------------------------------------
/doc/preferences.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/doc/preferences.png
--------------------------------------------------------------------------------
/doc/run_configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/doc/run_configuration.png
--------------------------------------------------------------------------------
/doc/test_results.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/doc/test_results.png
--------------------------------------------------------------------------------
/intellij-nette-tester.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/intellij-nette-tester.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/intellij-nette-tester.jar
--------------------------------------------------------------------------------
/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | cz.jiripudil.intellij.nette.tester
3 | Nette Tester
4 | 2.1.0
5 | Jiří Pudil
6 | messages.TesterBundle
7 |
8 | Github
10 |
11 | This plugin integrates Nette Tester
12 | into PhpStorm IDE.
13 | ]]>
14 |
15 | 2.1.0
17 |
18 | Added run line markers to run TestCase or test methods
19 | Added support for run configuration in Docker
20 | Added support for Nette Tester 2.0
21 | Fixed Compatibility with PHPStorm 2019.2 (#43 )
22 | Fixed bug java.lang.NullPointerException on phpstorm startup (#45 )
23 |
24 |
25 | 2.0.0-beta.3
26 |
27 | The Create Test dialog does not autoupdate the target namespace and directory if invoked from the directory context (#26 )
28 | Added some handy inspections that make sure all your tests are actually executed (#3 , #32 )
29 | You can right-click a single test case file to run it (#23 )
30 | Added support for nette/tester@dev-master
for the time being (#30 )
31 | Fixed: bootstrap relative path resolves correctly if the target directory does not exist (#25 )
32 | Fixed: comparison failure reporting was broken on Unix systems
33 |
34 |
35 | 2.0.0-beta.2
36 | I know I said this release branch would be feature-frozen, but... well... not just yet. I'm far too excited about bringing the new features to be able to postpone them. Semver doesn't play well with the plugin's distribution channel anyway. Here they come:
37 |
38 | In the project settings, you can configure the path to your bootstrap file; it is then automatically required in the created test files (see #20 for instructions if it does not work right away)
39 | You can now right-click a single test method to run or debug it in isolation (#9 )
40 | Fixed: OutputHandler now recognizes and correctly parses the diff command generated on Windows (#21 )
41 | Fixed: OutputHandler no longer reports unsuccessful builds as "terminated" (#22 )
42 |
43 |
44 | 2.0.0-beta.1
45 | After tens of hours of work, here comes a total rework of the plugin. This beta.1 release marks the feature freeze, now I'd like to focus on fixing bugs and releasing a stable version soon.
46 |
47 | Most importantly, it no longer relies on a specific fork nette/tester. As of 2.0.0, the plugin supports all 1.* versions of Nette Tester. Hooray!
48 | You can navigate between the class and its test, or create a test case for a class easily. The navigation is based on convention (class name + Test
suffix), the creation assumes your tests reside in the same namespace as the code; if you use a different scheme, you can configure source to test namespace mapping in the project settings under PHP > Nette Tester.
49 | The plugin ships with a run configuration producer for directories, so you can right-click a directory and run the tests within it. For this to be truly one-click action, you should configure the Tester executable in the Nette Tester default run configuration template.
50 | You can view the diff of comparison failures (Assert::same()
and alike) right in the test results window.
51 | Output stack traces are decorated with links to files, provided you set Tester\Dumper::$maxPathSegments
to a high enough value in your test environment.
52 |
53 | To support a wide range of Tester versions, I had to drop a few features. However, they should, in some form or another, be back once OutputHandler refactoring is resolved.
54 |
55 | The plugin no longer groups TestCase methods in the test results window.
56 | The plugin no longer lets you navigate to the tests from the test results window, nor run or debug them directly.
57 |
58 |
59 | 1.0.0-alpha.4
60 |
61 | Fixed compatibility with PhpStorm 2016.3.
62 |
63 |
64 | 1.0.0-alpha.3
65 |
66 | Fixed compatibility with PhpStorm 2016.3.
67 |
68 |
69 | 1.0-alpha2
70 |
71 | Fixed compatibility with PhpStorm 2016.1.
72 |
73 |
74 | 1.0-alpha
75 | Initial alpha version with basic capabilities:
76 |
77 | Tester run configuration type and editor.
78 | Displays results in the Test Runner window.
79 |
80 | ]]>
81 |
82 |
83 |
84 | com.intellij.modules.lang
85 | com.jetbrains.php
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
113 |
114 |
120 |
126 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/resources/fileTemplates/internal/Tester TestCase.ft:
--------------------------------------------------------------------------------
1 | run();
27 |
--------------------------------------------------------------------------------
/resources/icons/run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/resources/icons/run.png
--------------------------------------------------------------------------------
/resources/icons/runClass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/resources/icons/runClass.png
--------------------------------------------------------------------------------
/resources/icons/runMethod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/resources/icons/runMethod.png
--------------------------------------------------------------------------------
/resources/icons/testConfig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-tester/ece7132bd107c93af2f956f183ff2a23efa0f89e/resources/icons/testConfig.png
--------------------------------------------------------------------------------
/resources/inspectionDescriptions/TestCaseAnnotation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tester test cases can run in parallel if annotated.
4 |
5 | By annotating your test case with @testCase
tag, you can instrument Nette Tester to run each of the contained test methods in a separate thread.
6 |
7 |
--------------------------------------------------------------------------------
/resources/inspectionDescriptions/TestCaseIsRun.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tester test cases must be run manually.
4 |
5 | In the test file, the test case must be instantiated and executed via the run()
method in order to actually run the tests.
6 |
7 |
--------------------------------------------------------------------------------
/resources/inspectionDescriptions/TestFileName.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tester test case files must have correct names.
4 |
5 | Nette Tester searches for files with the phpt
extension, or, starting from 2.0, files with the php
extension and Test
name suffix.
6 |
7 |
--------------------------------------------------------------------------------
/resources/messages/TesterBundle.properties:
--------------------------------------------------------------------------------
1 | # actions and dialogs
2 |
3 | action.generateSetupMethod.name=TestCase setUp() method
4 | action.generateSetupMethod.description=Creates a TestCase setUp() method
5 |
6 | action.runTestAction.name=Run
7 | action.createRunTestAction.name=Create
8 |
9 | action.generateTeardownMethod.name=TestCase tearDown() method
10 | action.generateTeardownMethod.description=Creates a TestCase tearDown() method
11 |
12 | action.generateTestMethod.name=TestCase test() Method
13 | action.generateTestMethod.description=Creates a TestCase test() method
14 |
15 | action.newTestCase.name=Nette Tester TestCase
16 | action.newTestCase.description=Creates a new Nette Tester TestCase
17 |
18 | dialog.newTestCase.title=Create Nette Tester Test
19 | dialog.newTestCase.completionShortcut=Use {0} for {1} completion
20 | dialog.newTestCase.label.classToTest=Class to Test:
21 | dialog.newTestCase.label.testClass=Test name:
22 | dialog.newTestCase.label.namespace=Namespace:
23 | dialog.newTestCase.label.fileName=File name:
24 | dialog.newTestCase.label.directory=Directory:
25 |
26 |
27 | # configuration types
28 |
29 | configurationType.displayName=Nette Tester
30 | configurationType.description=Run tests via Nette Tester
31 |
32 | configurationType.method.displayName=Nette Tester test method
33 | configurationType.method.description=Run a single Nette Tester TestCase test method
34 |
35 |
36 | # run configuration settings
37 |
38 | runConfiguration.errors.phpInterpreterNotSet=PHP interpreter for current project is not set or was not be found.
39 | runConfiguration.errors.phpExecutableNotFound=PHP executable of the project PHP interpreter was not found.
40 | runConfiguration.errors.noTestScope=You must specify the test scope.
41 | runConfiguration.errors.testScopeNotFound=File or directory set as the test scope was not found.
42 | runConfiguration.errors.testEnvInterpreterNotFound=Test environment PHP interpreter was not found.
43 | runConfiguration.errors.testEnvExecutableNotFound=Test environment PHP executable was not found.
44 | runConfiguration.errors.noExecutable=You must specify the path to Tester executable.
45 | runConfiguration.errors.executableNotFound=Tester executable was not found at given path.
46 | runConfiguration.errors.phpIniNotFound=The php.ini file was not found at given path.
47 | runConfiguration.errors.setupScriptNotFound=The setup script file was not found at given path.
48 |
49 | runConfiguration.editor.tester.title=Nette Tester
50 | runConfiguration.editor.tester.testScope=Test scope:
51 | runConfiguration.editor.tester.testerExecutable=Tester executable:
52 | runConfiguration.editor.tester.testerOptions=Tester options:
53 | runConfiguration.editor.tester.setupScript=Setup script:
54 |
55 | runConfiguration.editor.testEnv.title=Test Environment
56 | runConfiguration.editor.testEnv.interpreter=Interpreter:
57 | runConfiguration.editor.testEnv.interpreterOptions=Interpreter options:
58 | runConfiguration.editor.testEnv.phpIni=Path to php.ini
59 | runConfiguration.editor.testEnv.useSystemPhpIni=Use system php.ini
60 |
61 | runConfiguration.editor.cli.title=Command Line
62 | runConfiguration.editor.cli.interpreterOptions=Interpreter options:
63 | runConfiguration.editor.cli.workingDirectory=Custom working directory:
64 |
65 | runConfiguration.mainConfiguration.missing.title=Run configuration 'tester' missing
66 | runConfiguration.mainConfiguration.missing.description=To use line markers, you need create run configuration named 'tester' with interpreter.
67 |
68 | runConfiguration.mainConfiguration.invalid.title=Run configuration 'tester' is invalid
69 | runConfiguration.mainConfiguration.invalid.description=To use line markers, you need valid run configuration named 'tester'.
70 |
71 | runConfiguration.mainConfiguration.alreadyCreated.title=Run configuration already created
72 | runConfiguration.mainConfiguration.alreadyCreated.description=Run configuration already exists. It can not be created again.
73 |
74 | runConfiguration.mainConfiguration.created.title=Run configuration created
75 | runConfiguration.mainConfiguration.created.description=Run configuration was successfully created.
76 |
77 |
78 | # project settings
79 |
80 | settings.defaultExtension=Default extension:
81 | settings.bootstrapFile=Bootstrap file:
82 | settings.testerVersion=Tester version:
83 | settings.namespaceMappings.title=Namespace mappings
84 | settings.namespaceMappings.noMappings=No mappings
85 | settings.namespaceMappings.sourceNamespace=Source namespace
86 | settings.namespaceMappings.testsNamespace=Tests namespace
87 |
88 |
89 | # inspections
90 |
91 | inspections.familyName=Nette Tester
92 |
93 | inspections.fileName.description=Test case file has an incorrect name
94 | inspections.fileName.quickFix.phpt=Change extension to 'phpt'
95 | inspections.fileName.quickFix.test=Add 'Test' suffix to file name
96 |
97 | inspections.annotation.description=Test case should have @testCase annotation
98 | inspections.annotation.quickFix=Annotate class with @testCase
99 |
100 | inspections.runTestCase.description=Test case is not run
101 | inspections.runTestCase.addRunMethodCall.quickFix=Add run() method call
102 | inspections.runTestCase.makeAbstract.quickFix=Make test case abstract
103 |
--------------------------------------------------------------------------------
/resources/setup.php:
--------------------------------------------------------------------------------
1 | file = \fopen($output, 'w');
25 | }
26 |
27 |
28 | public function begin()
29 | {
30 | // \fwrite($this->file, $this->message('testCount', array('count' => 0)));
31 | }
32 |
33 |
34 | public function result($testName, $result, $message)
35 | {
36 | $flowId = \md5($testName);
37 | \fwrite($this->file, $this->message('testStarted', array('name' => $testName, 'flowId' => $flowId)));
38 |
39 | if ($result === 2) { // Runner::SKIPPED, Test::SKIPPED
40 | \fwrite($this->file, $this->message('testIgnored', array('name' => $testName, 'flowId' => $flowId, 'message' => 'Test skipped', 'details' => $message)));
41 |
42 | } elseif ($result === 3) { // Runner::FAILED, Test::FAILED
43 | $extraArguments = array();
44 | if (\preg_match("/^diff \"(.*)\" \"(.*)\"$/m", $message, $matches)) { // Windows build
45 | $expectedFile = \str_replace('""', '"', $matches[1]);
46 | $actualFile = \str_replace('""', '"', $matches[2]);
47 | $extraArguments = array('type' => 'comparisonFailure', 'expectedFile' => $expectedFile, 'actualFile' => $actualFile);
48 |
49 | } elseif (\preg_match("/^diff '?(.*)'? '?(.*)'?$/m", $message, $matches)) {
50 | $expectedFile = \trim($matches[1], "'");
51 | $actualFile = \trim($matches[2], "'");
52 | $extraArguments = array('type' => 'comparisonFailure', 'expectedFile' => $expectedFile, 'actualFile' => $actualFile);
53 |
54 | } elseif (\preg_match("/Failed: (.*) should be( equal to)?\s+\.*\s*(.*) in/is", $message, $matches)) {
55 | $expected = $matches[3];
56 | $actual = $matches[1];
57 | $extraArguments = array('type' => 'comparisonFailure', 'expected' => $expected, 'actual' => $actual);
58 | }
59 |
60 | $args = \array_merge(array(
61 | 'name' => $testName,
62 | 'flowId' => $flowId,
63 | 'message' => 'Test failed',
64 | 'details' => $message,
65 | ), $extraArguments);
66 |
67 | \fwrite($this->file, $this->message('testFailed', $args));
68 | }
69 |
70 | \fwrite($this->file, $this->message('testFinished', array('name' => $testName, 'flowId' => $flowId)));
71 | }
72 |
73 |
74 | public function end()
75 | {
76 | }
77 |
78 |
79 | private function message($messageName, $args)
80 | {
81 | $argsPairs = array();
82 | foreach ($args as $arg => $value) {
83 | $argsPairs[] = \sprintf("%s='%s'", $arg, $this->escape($value));
84 | }
85 |
86 | return \sprintf(
87 | "##teamcity[%s %s]\n\n",
88 | $messageName,
89 | \implode(' ', $argsPairs)
90 | );
91 | }
92 |
93 |
94 | private function escape($value)
95 | {
96 | $replace = array(
97 | "|" => "||",
98 | "'" => "|'",
99 | "\n" => "|n",
100 | "\r" => "|r",
101 | "]" => "|]",
102 | "[" => "|[",
103 | );
104 |
105 | return \strtr($value, $replace);
106 | }
107 |
108 | }
109 |
110 |
111 | /** @var Runner $runner */
112 | // replace registered output handlers with TC
113 | $runner->outputHandlers = array();
114 | $runner->outputHandlers[] = new TeamCityOutputHandler();
115 |
--------------------------------------------------------------------------------
/resources/setup2-0.php:
--------------------------------------------------------------------------------
1 | file = \fopen($output, 'w');
25 | }
26 |
27 |
28 | public function begin(): void
29 | {
30 | // \fwrite($this->file, $this->message('testCount', array('count' => 0)));
31 | }
32 |
33 | function prepare(\Tester\Runner\Test $test): void
34 | {
35 | // TODO: Implement prepare() method.
36 | }
37 |
38 | function finish(\Tester\Runner\Test $test): void
39 | {
40 | $testName = $test->title ?: $test->getFile();
41 | $result = $test->getResult();
42 | $message = $test->message;
43 |
44 | $flowId = \md5($testName);
45 | \fwrite($this->file, $this->message('testStarted', array('name' => $testName, 'flowId' => $flowId)));
46 |
47 | if ($result === \Tester\Runner\Test::SKIPPED) { // Runner::SKIPPED, Test::SKIPPED
48 | \fwrite($this->file, $this->message('testIgnored', array('name' => $testName, 'flowId' => $flowId, 'message' => 'Test skipped', 'details' => $message)));
49 |
50 | } elseif ($result === \Tester\Runner\Test::FAILED) { // Runner::FAILED, Test::FAILED
51 | $extraArguments = array();
52 | if (\preg_match("/^diff \"(.*)\" \"(.*)\"$/m", $message, $matches)) { // Windows build
53 | $expectedFile = \str_replace('""', '"', $matches[1]);
54 | $actualFile = \str_replace('""', '"', $matches[2]);
55 | $extraArguments = array('type' => 'comparisonFailure', 'expectedFile' => $expectedFile, 'actualFile' => $actualFile);
56 |
57 | } elseif (\preg_match("/^diff '?(.*)'? '?(.*)'?$/m", $message, $matches)) {
58 | $expectedFile = \trim($matches[1], "'");
59 | $actualFile = \trim($matches[2], "'");
60 | $extraArguments = array('type' => 'comparisonFailure', 'expectedFile' => $expectedFile, 'actualFile' => $actualFile);
61 |
62 | } elseif (\preg_match("/Failed: (.*) should be( equal to)?\s+\.*\s*(.*) in/is", $message, $matches)) {
63 | $expected = $matches[3];
64 | $actual = $matches[1];
65 | $extraArguments = array('type' => 'comparisonFailure', 'expected' => $expected, 'actual' => $actual);
66 | }
67 |
68 | $args = \array_merge(array(
69 | 'name' => $testName,
70 | 'flowId' => $flowId,
71 | 'message' => 'Test failed',
72 | 'details' => $message,
73 | ), $extraArguments);
74 |
75 | \fwrite($this->file, $this->message('testFailed', $args));
76 | }
77 |
78 | \fwrite($this->file, $this->message('testFinished', array('name' => $testName, 'flowId' => $flowId)));
79 | }
80 |
81 |
82 | public function end(): void
83 | {
84 | }
85 |
86 |
87 | private function message($messageName, $args): string
88 | {
89 | $argsPairs = array();
90 | foreach ($args as $arg => $value) {
91 | $argsPairs[] = \sprintf("%s='%s'", $arg, $this->escape($value));
92 | }
93 |
94 | return \sprintf(
95 | "##teamcity[%s %s]\n\n",
96 | $messageName,
97 | \implode(' ', $argsPairs)
98 | );
99 | }
100 |
101 |
102 | private function escape($value): string
103 | {
104 | $replace = array(
105 | "|" => "||",
106 | "'" => "|'",
107 | "\n" => "|n",
108 | "\r" => "|r",
109 | "]" => "|]",
110 | "[" => "|[",
111 | );
112 |
113 | return \strtr($value, $replace);
114 | }
115 |
116 | }
117 |
118 |
119 | /** @var Runner $runner */
120 | // replace registered output handlers with TC
121 | $runner->outputHandlers = array();
122 | $runner->outputHandlers[] = new TeamCityOutputHandler();
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/TesterBundle.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester;
2 |
3 | import com.intellij.CommonBundle;
4 | import org.jetbrains.annotations.NonNls;
5 | import org.jetbrains.annotations.NotNull;
6 | import org.jetbrains.annotations.PropertyKey;
7 |
8 | import java.util.ResourceBundle;
9 |
10 | public class TesterBundle {
11 | @NonNls private static final String BUNDLE_NAME = "messages.TesterBundle";
12 | @NotNull private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
13 |
14 | private TesterBundle() {
15 | }
16 |
17 | public static String message(@PropertyKey(resourceBundle = BUNDLE_NAME) String key, Object... params) {
18 | return CommonBundle.message(BUNDLE, key, params);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/TesterIcons.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester;
2 |
3 | import com.intellij.openapi.util.IconLoader;
4 |
5 | import javax.swing.*;
6 |
7 | public class TesterIcons {
8 | /** file icon */
9 | public static final Icon RUN_CLASS = IconLoader.getIcon("/icons/runClass.png");
10 | public static final Icon RUN_METHOD = IconLoader.getIcon("/icons/runMethod.png");
11 | public static final Icon RUN = IconLoader.getIcon("/icons/run.png");
12 | public static final Icon TESTER_CONFIG = IconLoader.getIcon("/icons/testConfig.png");
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/TesterPostStartupActivity.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.startup.StartupActivity;
5 | import com.intellij.testIntegration.LanguageTestCreators;
6 | import com.jetbrains.php.lang.PhpLanguage;
7 | import cz.jiripudil.intellij.nette.tester.codeGeneration.TesterTestCreator;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class TesterPostStartupActivity implements StartupActivity {
11 | @Override
12 | public void runActivity(@NotNull Project project) {
13 | LanguageTestCreators.INSTANCE.addExplicitExtension(PhpLanguage.INSTANCE, TesterTestCreator.INSTANCE);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/TesterTestFinder.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.text.StringUtil;
5 | import com.intellij.psi.PsiElement;
6 | import com.intellij.psi.PsiFile;
7 | import com.intellij.psi.search.FilenameIndex;
8 | import com.intellij.psi.search.GlobalSearchScope;
9 | import com.intellij.psi.util.PsiTreeUtil;
10 | import com.intellij.testIntegration.TestFinder;
11 | import com.jetbrains.php.PhpIndex;
12 | import com.jetbrains.php.lang.psi.elements.PhpClass;
13 | import com.jetbrains.php.lang.psi.elements.PhpNamedElement;
14 | import com.jetbrains.php.lang.psi.elements.PhpPsiElement;
15 | import org.jetbrains.annotations.NotNull;
16 | import org.jetbrains.annotations.Nullable;
17 |
18 | import java.util.ArrayList;
19 | import java.util.Arrays;
20 | import java.util.Collection;
21 |
22 | public class TesterTestFinder implements TestFinder {
23 | @Nullable
24 | @Override
25 | public PsiElement findSourceElement(@NotNull PsiElement psiElement) {
26 | return PsiTreeUtil.getParentOfType(psiElement, PhpNamedElement.class, false);
27 | }
28 |
29 | @NotNull
30 | @Override
31 | public Collection findTestsForClass(@NotNull PsiElement psiElement) {
32 | Collection tests = new ArrayList<>();
33 |
34 | PhpClass phpClass = PsiTreeUtil.getStubOrPsiParentOfType(psiElement, PhpClass.class);
35 | if (phpClass != null) {
36 | Project project = phpClass.getProject();
37 | Collection testClasses = PhpIndex.getInstance(project).getClassesByName(phpClass.getName() + "Test");
38 | if (!testClasses.isEmpty()) {
39 | tests.addAll(testClasses);
40 | }
41 |
42 | PsiFile[] files = FilenameIndex.getFilesByName(project, phpClass.getName() + ".phpt", GlobalSearchScope.projectScope(project));
43 | tests.addAll(Arrays.asList(files));
44 | }
45 |
46 | return tests;
47 | }
48 |
49 | @NotNull
50 | @Override
51 | public Collection findClassesForTest(@NotNull PsiElement psiElement) {
52 | Collection classes = new ArrayList<>();
53 |
54 | PhpClass testClass = PsiTreeUtil.getStubOrPsiParentOfType(psiElement, PhpClass.class);
55 | if (testClass != null && StringUtil.endsWith(testClass.getName(), "Test")) {
56 | Project project = testClass.getProject();
57 | Collection sourceClasses = PhpIndex.getInstance(project).getClassesByName(testClass.getName().substring(0, testClass.getName().length() - "Test".length()));
58 | if (!sourceClasses.isEmpty()) {
59 | classes.addAll(sourceClasses);
60 | }
61 | }
62 |
63 | PsiFile containingFile = psiElement.getContainingFile();
64 | if (containingFile != null) {
65 | Project project = containingFile.getProject();
66 | String name = containingFile.getVirtualFile().getNameWithoutExtension();
67 | if (StringUtil.endsWith(name, "Test")) {
68 | name = name.substring(0, name.length() - "Test".length());
69 | }
70 |
71 | Collection sourceClasses = PhpIndex.getInstance(project).getClassesByName(name);
72 | classes.addAll(sourceClasses);
73 | }
74 |
75 | return classes;
76 | }
77 |
78 | @Override
79 | public boolean isTest(@NotNull PsiElement psiElement) {
80 | PsiFile containingFile = psiElement.getContainingFile();
81 | return containingFile instanceof PhpPsiElement && (
82 | (containingFile.getVirtualFile() != null && containingFile.getVirtualFile().getExtension() != null && containingFile.getVirtualFile().getExtension().equals("phpt"))
83 | || StringUtil.endsWith(containingFile.getName(), "Test")
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/TesterUtil.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester;
2 |
3 | import com.intellij.execution.RunManager;
4 | import com.intellij.execution.RunnerAndConfigurationSettings;
5 | import com.intellij.execution.configurations.RunConfiguration;
6 | import com.intellij.execution.configurations.RuntimeConfigurationException;
7 | import com.intellij.notification.Notification;
8 | import com.intellij.notification.NotificationType;
9 | import com.intellij.notification.Notifications;
10 | import com.intellij.openapi.application.Application;
11 | import com.intellij.openapi.application.ApplicationManager;
12 | import com.intellij.openapi.project.Project;
13 | import com.intellij.openapi.util.Ref;
14 | import com.intellij.openapi.util.text.StringUtil;
15 | import com.jetbrains.php.PhpClassHierarchyUtils;
16 | import com.jetbrains.php.lang.PhpLangUtil;
17 | import com.jetbrains.php.lang.psi.elements.Method;
18 | import com.jetbrains.php.lang.psi.elements.PhpClass;
19 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
20 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfigurationType;
21 | import cz.jiripudil.intellij.nette.tester.configuration.TesterTestMethodRunConfiguration;
22 | import cz.jiripudil.intellij.nette.tester.configuration.TesterTestMethodRunConfigurationType;
23 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterProjectSettings;
24 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterProjectSettingsManager;
25 | import org.jetbrains.annotations.Nls;
26 | import org.jetbrains.annotations.NotNull;
27 | import org.jetbrains.annotations.Nullable;
28 |
29 | import java.util.ArrayList;
30 | import java.util.List;
31 |
32 | public class TesterUtil {
33 | public static String NOTIFICATION_GROUP = "Nette tester";
34 |
35 | public static boolean isTestClass(@NotNull PhpClass phpClass) {
36 | if (phpClass.isAbstract() || phpClass.isInterface() || phpClass.isTrait()) {
37 | return false;
38 | }
39 |
40 | final Ref isTestCase = new Ref<>(false);
41 | PhpClassHierarchyUtils.processSuperClasses(phpClass, true, true, phpClass1 -> {
42 | String superFQN = phpClass1.getSuperFQN();
43 | if (superFQN != null && PhpLangUtil.equalsClassNames("\\Tester\\TestCase", superFQN)) {
44 | isTestCase.set(true);
45 | }
46 |
47 | return !isTestCase.get();
48 | });
49 |
50 | return isTestCase.get();
51 | }
52 |
53 | public static TesterProjectSettings getTesterSettings(@NotNull Project project) {
54 | return TesterProjectSettingsManager.getInstance(project).getState();
55 | }
56 |
57 | public static boolean isTestMethod(@NotNull Method method) {
58 | return method.getContainingClass() != null
59 | && isTestClass(method.getContainingClass())
60 | && StringUtil.startsWith(method.getName(), "test")
61 | && method.getModifier().isPublic();
62 | }
63 |
64 | @Nullable
65 | public static TesterRunConfiguration getMainConfiguration(@NotNull Project project) {
66 | List configurations = TesterUtil.getRunConfigurations(project);
67 | return getMainConfiguration(project, configurations);
68 | }
69 |
70 | @Nullable
71 | public static TesterRunConfiguration getMainConfiguration(
72 | @NotNull Project project,
73 | @NotNull List configurations
74 | ) {
75 | @Nullable TesterRunConfiguration mainConfiguration = configurations.stream()
76 | .filter(configuration -> "tests".equals(configuration.getName()))
77 | .findAny()
78 | .orElse(null);
79 | if (mainConfiguration == null) {
80 | TesterUtil.doNotify(
81 | TesterBundle.message("runConfiguration.mainConfiguration.missing.title"),
82 | TesterBundle.message("runConfiguration.mainConfiguration.missing.description"),
83 | NotificationType.ERROR,
84 | project
85 | );
86 | return null;
87 |
88 | } else {
89 | try {
90 | mainConfiguration.checkConfiguration();
91 | return mainConfiguration;
92 |
93 | } catch (RuntimeConfigurationException ex) {
94 | TesterUtil.doNotify(
95 | TesterBundle.message("runConfiguration.mainConfiguration.invalid.title"),
96 | TesterBundle.message("runConfiguration.mainConfiguration.invalid.description"),
97 | NotificationType.ERROR,
98 | project
99 | );
100 | }
101 | return null;
102 | }
103 | }
104 |
105 | public static List getRunConfigurations(@NotNull Project project) {
106 | List configurations = new ArrayList();
107 | List settings = RunManager.getInstance(project)
108 | .getConfigurationSettingsList(TesterRunConfigurationType.class);
109 | for (RunnerAndConfigurationSettings setting : settings) {
110 | RunConfiguration configuration = setting.getConfiguration();
111 | if (configuration instanceof TesterRunConfiguration) {
112 | configurations.add((TesterRunConfiguration) configuration);
113 | }
114 | }
115 | return configurations;
116 | }
117 |
118 | public static List getMethodRunConfigurations(@NotNull Project project) {
119 | List configurations = new ArrayList();
120 | List settings = RunManager.getInstance(project)
121 | .getConfigurationSettingsList(TesterTestMethodRunConfigurationType.class);
122 | for (RunnerAndConfigurationSettings setting : settings) {
123 | RunConfiguration configuration = setting.getConfiguration();
124 | if (configuration instanceof TesterTestMethodRunConfiguration) {
125 | configurations.add((TesterTestMethodRunConfiguration) configuration);
126 | }
127 | }
128 | return configurations;
129 | }
130 |
131 | public static void doNotify(
132 | @NotNull String title,
133 | @NotNull @Nls(capitalization = Nls.Capitalization.Sentence) String content,
134 | @NotNull NotificationType type,
135 | @Nullable Project project
136 | ) {
137 | Notification notification = new Notification(NOTIFICATION_GROUP, title, content, NotificationType.ERROR);
138 | doNotify(notification, project);
139 | }
140 |
141 | public static void doNotify(Notification notification, @Nullable Project project) {
142 | if (project != null && !project.isDisposed() && !project.isDefault()) {
143 | ((Notifications)project.getMessageBus().syncPublisher(Notifications.TOPIC)).notify(notification);
144 | } else {
145 | Application app = ApplicationManager.getApplication();
146 | if (!app.isDisposed()) {
147 | ((Notifications)app.getMessageBus().syncPublisher(Notifications.TOPIC)).notify(notification);
148 | }
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/action/BaseTesterRunAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.action;
2 |
3 | import com.intellij.execution.RunnerAndConfigurationSettings;
4 | import com.intellij.ide.actions.runAnything.RunAnythingAction;
5 | import com.intellij.notification.NotificationType;
6 | import com.intellij.openapi.actionSystem.AnActionEvent;
7 | import com.intellij.openapi.project.Project;
8 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
9 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
10 | import org.jetbrains.annotations.NotNull;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | abstract public class BaseTesterRunAction extends RunAnythingAction {
14 |
15 | protected Project project;
16 | protected boolean wasCreated = false;
17 |
18 | BaseTesterRunAction(@NotNull Project project) {
19 | this.project = project;
20 | }
21 |
22 | @Nullable
23 | abstract protected RunnerAndConfigurationSettings prepareAction();
24 |
25 | protected boolean isTemporary() {
26 | return false;
27 | }
28 |
29 | protected Project getProject() {
30 | return project;
31 | }
32 |
33 | @Override
34 | public void actionPerformed(@NotNull AnActionEvent e) {
35 | prepareAction();
36 |
37 | String title;
38 | String description;
39 | if (!wasCreated) {
40 | title = TesterBundle.message("runConfiguration.mainConfiguration.alreadyCreated.title");
41 | description = TesterBundle.message("runConfiguration.mainConfiguration.alreadyCreated.description");
42 | } else {
43 | title = TesterBundle.message("runConfiguration.mainConfiguration.created.title");
44 | description = TesterBundle.message("runConfiguration.mainConfiguration.created.description");
45 | }
46 | TesterUtil.doNotify(title, description, NotificationType.INFORMATION, project);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/action/TesterCreateMethodRunTestAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.action;
2 |
3 | import com.intellij.execution.RunManager;
4 | import com.intellij.execution.RunnerAndConfigurationSettings;
5 | import com.intellij.execution.configurations.ConfigurationFactory;
6 | import com.intellij.execution.configurations.RuntimeConfigurationException;
7 | import com.intellij.psi.PsiElement;
8 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
9 | import cz.jiripudil.intellij.nette.tester.TesterIcons;
10 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
11 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
12 | import cz.jiripudil.intellij.nette.tester.configuration.TesterTestMethodRunConfiguration;
13 | import cz.jiripudil.intellij.nette.tester.configuration.TesterTestMethodRunConfigurationType;
14 | import org.jetbrains.annotations.NotNull;
15 | import org.jetbrains.annotations.Nullable;
16 |
17 | import java.util.List;
18 | import java.util.stream.Collectors;
19 |
20 | public class TesterCreateMethodRunTestAction extends BaseTesterRunAction {
21 | private final String path;
22 | private final String methodName;
23 | private final String testName;
24 |
25 | public TesterCreateMethodRunTestAction(@NotNull PsiElement element, @NotNull String testName, @NotNull String testMethod) {
26 | super(element.getProject());
27 |
28 | this.path = element.getContainingFile().getVirtualFile().getPath();
29 | this.testName = testName;
30 | this.methodName = testMethod;
31 |
32 | getTemplatePresentation().setText(TesterBundle.message("action.createRunTestAction.name") + " '" + testMethod + "()'...");
33 | getTemplatePresentation().setIcon(TesterIcons.TESTER_CONFIG);
34 | }
35 |
36 | @Nullable
37 | protected RunnerAndConfigurationSettings prepareAction() {
38 | wasCreated = false;
39 | List configurations = TesterUtil.getMethodRunConfigurations(project);
40 | List samePath = configurations.stream()
41 | .filter(configuration -> {
42 | if (configuration.getMethod() == null) {
43 | return false;
44 | }
45 |
46 | try {
47 | configuration.checkConfiguration();
48 |
49 | } catch (RuntimeConfigurationException ex) {
50 | return false;
51 | }
52 | return path.equals(configuration.getSettings().getPath())
53 | && configuration.getMethod().startsWith(methodName);
54 | })
55 | .collect(Collectors.toList());
56 |
57 | RunManager manager = RunManager.getInstance(project);
58 | if (samePath.size() == 0) {
59 | TesterRunConfiguration mainConfiguration = TesterUtil.getMainConfiguration(project);
60 | if (mainConfiguration == null) {
61 | return null;
62 | }
63 |
64 | ConfigurationFactory factory = TesterTestMethodRunConfigurationType.createFactory();
65 | TesterTestMethodRunConfiguration current = new TesterTestMethodRunConfiguration(
66 | project,
67 | factory,
68 | getConfigurationName()
69 | );
70 | current.getSettings().setPath(path);
71 | current.setMethodName(methodName);
72 |
73 | //todo: get more information from mainConfiguration and save to method configuration
74 | current.getSettings().setCommandLineSettings(mainConfiguration.getSettings().getPhpCommandLineSettings());
75 |
76 | RunnerAndConfigurationSettings action = manager.createConfiguration(current, factory);
77 | action.setTemporary(isTemporary());
78 |
79 | manager.addConfiguration(action);
80 | manager.setSelectedConfiguration(action);
81 | wasCreated = true;
82 | return action;
83 |
84 | } else {
85 | TesterTestMethodRunConfiguration existing = samePath.get(0);
86 | if (existing.getFactory() == null) {
87 | return null;
88 | }
89 | RunnerAndConfigurationSettings action = manager.createConfiguration(existing, existing.getFactory());
90 | manager.setSelectedConfiguration(action);
91 | return action;
92 | }
93 | }
94 |
95 | private String getConfigurationName() {
96 | return "tests:" + testName + ":" + TesterTestMethodRunConfiguration.createSuggestedName(methodName).trim();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/action/TesterCreateRunTestAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.action;
2 |
3 | import com.intellij.execution.RunManager;
4 | import com.intellij.execution.RunnerAndConfigurationSettings;
5 | import com.intellij.execution.configurations.RuntimeConfigurationException;
6 | import com.intellij.psi.PsiElement;
7 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
8 | import cz.jiripudil.intellij.nette.tester.TesterIcons;
9 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
10 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | import java.util.List;
15 | import java.util.stream.Collectors;
16 |
17 | public class TesterCreateRunTestAction extends BaseTesterRunAction {
18 | private final String path;
19 | private final String testName;
20 |
21 | public TesterCreateRunTestAction(@NotNull PsiElement element, @NotNull String testName) {
22 | super(element.getProject());
23 | this.path = element.getContainingFile().getVirtualFile().getPath();
24 | this.testName = testName;
25 |
26 | getTemplatePresentation().setText(TesterBundle.message("action.createRunTestAction.name") + " '" + testName + "'...");
27 | getTemplatePresentation().setIcon(TesterIcons.TESTER_CONFIG);
28 | }
29 |
30 | @Nullable
31 | protected RunnerAndConfigurationSettings prepareAction() {
32 | wasCreated = false;
33 | List configurations = TesterUtil.getRunConfigurations(getProject());
34 | List samePath = configurations.stream()
35 | .filter(configuration -> {
36 | try {
37 | configuration.checkConfiguration();
38 |
39 | } catch (RuntimeConfigurationException ex) {
40 | return false;
41 | }
42 | return path.equals(configuration.getSettings().getTestScope());
43 | })
44 | .collect(Collectors.toList());
45 |
46 | RunManager manager = RunManager.getInstance(getProject());
47 | if (samePath.size() == 0) {
48 | TesterRunConfiguration mainConfiguration = TesterUtil.getMainConfiguration(getProject(), configurations);
49 | if (mainConfiguration == null || mainConfiguration.getFactory() == null) {
50 | return null;
51 | }
52 |
53 | TesterRunConfiguration current = (TesterRunConfiguration) mainConfiguration.clone();
54 | current.setName("tests:" + testName);
55 | current.getSettings().setTestScope(path);
56 | RunnerAndConfigurationSettings action = manager.createConfiguration(current, mainConfiguration.getFactory());
57 | action.setTemporary(isTemporary());
58 |
59 | manager.addConfiguration(action);
60 | manager.setSelectedConfiguration(action);
61 | wasCreated = true;
62 | return action;
63 |
64 | } else {
65 | TesterRunConfiguration existing = samePath.get(0);
66 | if (existing.getFactory() == null) {
67 | return null;
68 | }
69 |
70 | RunnerAndConfigurationSettings action = manager.createConfiguration(existing, existing.getFactory());
71 | manager.setSelectedConfiguration(action);
72 | return action;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/action/TesterMethodRunTestAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.action;
2 |
3 | import com.intellij.execution.ProgramRunnerUtil;
4 | import com.intellij.execution.RunnerAndConfigurationSettings;
5 | import com.intellij.execution.executors.DefaultRunExecutor;
6 | import com.intellij.openapi.actionSystem.AnActionEvent;
7 | import com.intellij.psi.PsiElement;
8 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
9 | import cz.jiripudil.intellij.nette.tester.TesterIcons;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | public class TesterMethodRunTestAction extends TesterCreateMethodRunTestAction {
13 |
14 | public TesterMethodRunTestAction(@NotNull PsiElement element, @NotNull String testName, @NotNull String testMethod) {
15 | super(element, testName, testMethod);
16 |
17 | getTemplatePresentation().setText(TesterBundle.message("action.runTestAction.name") + " '" + testMethod + "()'");
18 | getTemplatePresentation().setIcon(TesterIcons.RUN);
19 | }
20 |
21 | protected boolean isTemporary() {
22 | return true;
23 | }
24 |
25 | @Override
26 | public void actionPerformed(@NotNull AnActionEvent e) {
27 | RunnerAndConfigurationSettings forRun = prepareAction();
28 | if (forRun != null) {
29 | ProgramRunnerUtil.executeConfiguration(forRun, DefaultRunExecutor.getRunExecutorInstance());
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/action/TesterRunTestAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.action;
2 |
3 | import com.intellij.execution.ProgramRunnerUtil;
4 | import com.intellij.execution.RunnerAndConfigurationSettings;
5 | import com.intellij.execution.executors.DefaultRunExecutor;
6 | import com.intellij.openapi.actionSystem.AnActionEvent;
7 | import com.intellij.psi.PsiElement;
8 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
9 | import cz.jiripudil.intellij.nette.tester.TesterIcons;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | public class TesterRunTestAction extends TesterCreateRunTestAction {
13 |
14 | public TesterRunTestAction(@NotNull PsiElement element, @NotNull String testName) {
15 | super(element, testName);
16 |
17 | getTemplatePresentation().setText(TesterBundle.message("action.runTestAction.name") + " '" + testName + "'");
18 | getTemplatePresentation().setIcon(TesterIcons.RUN);
19 | }
20 |
21 | protected boolean isTemporary() {
22 | return true;
23 | }
24 |
25 | @Override
26 | public void actionPerformed(@NotNull AnActionEvent e) {
27 | RunnerAndConfigurationSettings forRun = prepareAction();
28 | if (forRun != null) {
29 | ProgramRunnerUtil.executeConfiguration(forRun, DefaultRunExecutor.getRunExecutorInstance());
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterAbstractGenerateMethodAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.codeInsight.CodeInsightActionHandler;
4 | import com.intellij.codeInsight.actions.CodeInsightAction;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.util.TextRange;
8 | import com.intellij.psi.PsiDocumentManager;
9 | import com.intellij.psi.PsiElement;
10 | import com.intellij.psi.PsiFile;
11 | import com.jetbrains.php.lang.psi.PhpCodeEditUtil;
12 | import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
13 | import com.jetbrains.php.lang.psi.elements.Method;
14 | import com.jetbrains.php.lang.psi.elements.PhpClass;
15 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
16 | import org.jetbrains.annotations.NotNull;
17 | import org.jetbrains.annotations.Nullable;
18 |
19 | abstract public class TesterAbstractGenerateMethodAction extends CodeInsightAction implements CodeInsightActionHandler {
20 | @NotNull
21 | @Override
22 | protected CodeInsightActionHandler getHandler() {
23 | return this;
24 | }
25 |
26 | @Override
27 | protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
28 | return findTestClass(editor, file) != null;
29 | }
30 |
31 | @Nullable
32 | private static PhpClass findTestClass(@NotNull Editor editor, @NotNull PsiFile file) {
33 | PhpClass testClass = PhpCodeEditUtil.findClassAtCaret(editor, file);
34 | return testClass != null && TesterUtil.isTestClass(testClass) ? testClass : null;
35 | }
36 |
37 | @Override
38 | public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile psiFile) {
39 | PhpClass phpClass = findTestClass(editor, psiFile);
40 | Method method = PhpPsiElementFactory.createMethod(project, "public function testFoo(){}");
41 |
42 | assert phpClass != null;
43 |
44 | PsiElement element = PhpCodeEditUtil.insertClassMember(phpClass, method);
45 | PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
46 | TextRange range = element.getTextRange();
47 | editor.getDocument().replaceString(range.getStartOffset(), range.getEndOffset(), "");
48 | editor.getCaretModel().moveToOffset(range.getStartOffset());
49 | insertMethod(project, editor);
50 | }
51 |
52 | abstract protected void insertMethod(@NotNull Project project, @NotNull Editor editor);
53 | }
54 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterGenerateSetupMethodAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.codeInsight.template.Template;
4 | import com.intellij.codeInsight.template.TemplateManager;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.project.Project;
7 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class TesterGenerateSetupMethodAction extends TesterAbstractGenerateMethodAction {
11 | public TesterGenerateSetupMethodAction() {
12 | getTemplatePresentation().setText(TesterBundle.message("action.generateSetupMethod.name"));
13 | getTemplatePresentation().setDescription(TesterBundle.message("action.generateSetupMethod.description"));
14 | }
15 |
16 | @Override
17 | protected void insertMethod(@NotNull Project project, @NotNull Editor editor) {
18 | Template template = TemplateManager.getInstance(project).createTemplate("", "");
19 | template.addTextSegment("protected function setUp()\n{\n");
20 | template.addTextSegment("parent::setUp();\n");
21 | template.addEndVariable();
22 | template.addTextSegment("\n}");
23 | template.setToIndent(true);
24 | template.setToReformat(true);
25 | template.setToShortenLongNames(true);
26 | TemplateManager.getInstance(project).startTemplate(editor, template, null);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterGenerateTeardownMethodAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.codeInsight.template.Template;
4 | import com.intellij.codeInsight.template.TemplateManager;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.project.Project;
7 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class TesterGenerateTeardownMethodAction extends TesterAbstractGenerateMethodAction {
11 | public TesterGenerateTeardownMethodAction() {
12 | getTemplatePresentation().setText(TesterBundle.message("action.generateTeardownMethod.name"));
13 | getTemplatePresentation().setDescription(TesterBundle.message("action.generateTeardownMethod.description"));
14 | }
15 |
16 | @Override
17 | protected void insertMethod(@NotNull Project project, @NotNull Editor editor) {
18 | Template template = TemplateManager.getInstance(project).createTemplate("", "");
19 | template.addTextSegment("protected function tearDown()\n{\n");
20 | template.addTextSegment("parent::tearDown();\n");
21 | template.addEndVariable();
22 | template.addTextSegment("\n}");
23 | template.setToIndent(true);
24 | template.setToReformat(true);
25 | template.setToShortenLongNames(true);
26 | TemplateManager.getInstance(project).startTemplate(editor, template, null);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterGenerateTestMethodAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.codeInsight.template.Expression;
4 | import com.intellij.codeInsight.template.Template;
5 | import com.intellij.codeInsight.template.TemplateManager;
6 | import com.intellij.codeInsight.template.impl.ConstantNode;
7 | import com.intellij.openapi.editor.Editor;
8 | import com.intellij.openapi.project.Project;
9 | import com.jetbrains.php.config.PhpLanguageFeature;
10 | import com.jetbrains.php.config.PhpLanguageLevel;
11 | import com.jetbrains.php.config.PhpProjectConfigurationFacade;
12 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
13 | import org.jetbrains.annotations.NotNull;
14 |
15 | public class TesterGenerateTestMethodAction extends TesterAbstractGenerateMethodAction {
16 | public TesterGenerateTestMethodAction() {
17 | getTemplatePresentation().setText(TesterBundle.message("action.generateTestMethod.name"));
18 | getTemplatePresentation().setDescription(TesterBundle.message("action.generateTestMethod.description"));
19 | }
20 |
21 | @Override
22 | protected void insertMethod(@NotNull Project project, @NotNull Editor editor) {
23 | Template template = TemplateManager.getInstance(project).createTemplate("", "");
24 | template.addTextSegment("public function test");
25 |
26 | Expression nameExpr = new ConstantNode("");
27 | template.addVariable("name", nameExpr, nameExpr, true);
28 | template.addTextSegment("()");
29 |
30 | PhpLanguageLevel languageLevel = PhpProjectConfigurationFacade.getInstance(project).getLanguageLevel();
31 | if (languageLevel.hasFeature(PhpLanguageFeature.RETURN_VOID)) {
32 | template.addTextSegment(": void");
33 | }
34 |
35 | template.addTextSegment("\n{\n");
36 | template.addEndVariable();
37 | template.addTextSegment("\n}");
38 | template.setToIndent(true);
39 | template.setToReformat(true);
40 | template.setToShortenLongNames(true);
41 | TemplateManager.getInstance(project).startTemplate(editor, template, null);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterNamespaceMapper.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.text.StringUtil;
5 | import com.jetbrains.php.lang.psi.elements.PhpClass;
6 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
7 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterNamespaceMapping;
8 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterProjectSettings;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import java.util.List;
12 |
13 | public class TesterNamespaceMapper {
14 | @NotNull private final Project project;
15 |
16 | private TesterNamespaceMapper(@NotNull final Project project) {
17 | this.project = project;
18 | }
19 |
20 | public static TesterNamespaceMapper getInstance(@NotNull final Project project) {
21 | return new TesterNamespaceMapper(project);
22 | }
23 |
24 | @NotNull
25 | String mapSourceNamespaceToTestNamespace(@NotNull PhpClass sourceClass) {
26 | TesterProjectSettings settings = TesterUtil.getTesterSettings(project);
27 | String namespaceName = trimNamespaceSeparators(sourceClass.getNamespaceName());
28 | if (settings == null) {
29 | return namespaceName;
30 | }
31 |
32 | List mappings = settings.getNamespaceMappings();
33 | for (TesterNamespaceMapping mapping : mappings) {
34 | String fqn = trimNamespaceSeparators(sourceClass.getFQN());
35 | if (StringUtil.startsWith(fqn, mapping.getSourceNamespace() + "\\")) {
36 | return trimNamespaceSeparators(
37 | mapping.getTestsNamespace()
38 | + "\\" +
39 | trimNamespaceSeparators(namespaceName.substring(mapping.getSourceNamespace().length()))
40 | );
41 | }
42 | }
43 |
44 | return namespaceName;
45 | }
46 |
47 | @NotNull
48 | private String trimNamespaceSeparators(@NotNull String namespace) {
49 | return StringUtil.trimStart(StringUtil.trimEnd(namespace, "\\"), "\\");
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterNewTestCaseAction.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.psi.PsiDirectory;
5 | import com.intellij.psi.PsiFile;
6 | import com.jetbrains.php.PhpIcons;
7 | import com.jetbrains.php.actions.PhpNewBaseAction;
8 | import com.jetbrains.php.templates.PhpCreateFileFromTemplateDataProvider;
9 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
10 | import org.jetbrains.annotations.NotNull;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | public class TesterNewTestCaseAction extends PhpNewBaseAction {
14 | private final boolean fixedDirectory;
15 |
16 | public TesterNewTestCaseAction() {
17 | this(true);
18 | }
19 |
20 | TesterNewTestCaseAction(boolean fixedDirectory) {
21 | super(TesterBundle.message("action.newTestCase.name"), TesterBundle.message("action.newTestCase.description"), PhpIcons.PHP_TEST_FILE);
22 | this.fixedDirectory = fixedDirectory;
23 | }
24 |
25 | @Nullable
26 | @Override
27 | protected PhpCreateFileFromTemplateDataProvider getDataProvider(@NotNull Project project, @NotNull PsiDirectory psiDirectory, @Nullable PsiFile psiFile) {
28 | TesterNewTestCaseDialog dialog = new TesterNewTestCaseDialog(project, psiDirectory, psiFile, fixedDirectory);
29 | return dialog.showAndGet() ? dialog : null;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterNewTestCaseDialog.form:
--------------------------------------------------------------------------------
1 |
2 |
144 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterNewTestCaseDialog.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.openapi.editor.Editor;
4 | import com.intellij.openapi.editor.event.DocumentAdapter;
5 | import com.intellij.openapi.editor.event.DocumentEvent;
6 | import com.intellij.openapi.fileTypes.FileTypes;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.roots.ProjectFileIndex;
9 | import com.intellij.openapi.roots.ProjectRootManager;
10 | import com.intellij.openapi.util.text.StringUtil;
11 | import com.intellij.openapi.vfs.LocalFileSystem;
12 | import com.intellij.openapi.vfs.VirtualFile;
13 | import com.intellij.psi.PsiDirectory;
14 | import com.intellij.psi.PsiFile;
15 | import com.intellij.ui.EditorTextField;
16 | import com.intellij.ui.components.JBLabel;
17 | import com.intellij.util.ui.UIUtil;
18 | import com.jetbrains.php.PhpIndex;
19 | import com.jetbrains.php.actions.PhpBaseNewClassDialog;
20 | import com.jetbrains.php.completion.PhpCompletionUtil;
21 | import com.jetbrains.php.lang.PhpLangUtil;
22 | import com.jetbrains.php.lang.psi.elements.PhpClass;
23 | import com.jetbrains.php.roots.ui.PhpNamespaceComboBox;
24 | import com.jetbrains.php.roots.ui.PhpPsrDirectoryComboBox;
25 | import com.jetbrains.php.ui.PhpUiUtil;
26 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
27 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
28 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterProjectSettings;
29 | import org.jetbrains.annotations.NotNull;
30 | import org.jetbrains.annotations.Nullable;
31 |
32 | import javax.swing.*;
33 | import java.nio.file.Path;
34 | import java.nio.file.Paths;
35 | import java.util.Collection;
36 | import java.util.Properties;
37 |
38 | public class TesterNewTestCaseDialog extends PhpBaseNewClassDialog {
39 | private JPanel contentPane;
40 |
41 | private EditorTextField testTargetTextField;
42 | private JBLabel testTargetCompletionHint;
43 |
44 | private EditorTextField nameTextField;
45 | private PhpNamespaceComboBox namespaceComboBox;
46 | private JBLabel namespaceCompletionHint;
47 |
48 | private EditorTextField fileNameTextField;
49 | private PhpPsrDirectoryComboBox directoryComboBox;
50 | private JBLabel directoryCompletionHint;
51 |
52 | private JBLabel classToTestLabel;
53 | private JBLabel testClassLabel;
54 | private JBLabel namespaceLabel;
55 | private JBLabel fileNameLabel;
56 | private JBLabel directoryLabel;
57 |
58 | TesterNewTestCaseDialog(@NotNull Project project, @Nullable PsiDirectory directory, @Nullable PsiFile file, boolean fixedDirectory) {
59 | super(project, directory);
60 | this.init(contentPane, nameTextField, namespaceComboBox, namespaceCompletionHint, fileNameTextField, directoryComboBox, directoryCompletionHint);
61 |
62 | testTargetTextField.addDocumentListener(new DocumentAdapter() {
63 | @Override
64 | public void documentChanged(DocumentEvent e) {
65 | TesterNewTestCaseDialog.this.addUpdateRequest(() -> {
66 | String text = TesterNewTestCaseDialog.this.testTargetTextField.getText();
67 | if (!StringUtil.isEmpty(text) && !StringUtil.endsWith(text, "\\")) {
68 | PhpIndex instance = PhpIndex.getInstance(TesterNewTestCaseDialog.this.getProject());
69 | Collection classes = instance.getClassesByFQN(text);
70 | if (!classes.isEmpty()) {
71 | TesterNewTestCaseDialog.this.nameTextField.setText(PhpLangUtil.toShortName(text) + "Test");
72 | TesterNewTestCaseDialog.this.fileNameTextField.setText(PhpLangUtil.toShortName(text) + "Test");
73 |
74 | if (!fixedDirectory) {
75 | PhpClass phpClass = classes.iterator().next();
76 | String namespace = TesterNamespaceMapper.getInstance(project).mapSourceNamespaceToTestNamespace(phpClass);
77 | TesterNewTestCaseDialog.this.namespaceComboBox.getEditorTextField().setText(namespace);
78 | }
79 | }
80 | }
81 | });
82 | }
83 | });
84 | PhpCompletionUtil.installClassCompletion(testTargetTextField, null, this.getDisposable());
85 |
86 | String codeCompletionShortcut = PhpUiUtil.getShortcutTextByActionName("CodeCompletion");
87 | testTargetCompletionHint.setText(TesterBundle.message("dialog.newTestCase.completionShortcut", codeCompletionShortcut, "class reference"));
88 | namespaceCompletionHint.setText(TesterBundle.message("dialog.newTestCase.completionShortcut", codeCompletionShortcut, "namespace"));
89 | directoryCompletionHint.setText(TesterBundle.message("dialog.newTestCase.completionShortcut", codeCompletionShortcut, "path"));
90 |
91 | this.addUpdateRequest(() -> {
92 | PhpClass phpClass = TesterTestCreator.findClass(file);
93 | if (phpClass != null) {
94 | String fqn = phpClass.getFQN();
95 | this.testTargetTextField.setText(PhpLangUtil.toName(fqn));
96 | return;
97 | }
98 |
99 | this.nameTextField.setText("Test");
100 | Editor editor = this.nameTextField.getEditor();
101 | if (editor != null) {
102 | editor.getCaretModel().moveToOffset(0);
103 | }
104 | });
105 |
106 | this.init();
107 | }
108 |
109 | @Override
110 | protected void init() {
111 | super.init();
112 | this.setTitle(TesterBundle.message("dialog.newTestCase.title"));
113 | }
114 |
115 | @Nullable
116 | @Override
117 | protected String getDimensionServiceKey() {
118 | return "cz.jiripudil.intellij.nette.tester.codeGeneration.TesterNewTestCaseDialog";
119 | }
120 |
121 | @Nullable
122 | @Override
123 | public JComponent getPreferredFocusedComponent() {
124 | return testTargetTextField;
125 | }
126 |
127 | @NotNull
128 | @Override
129 | public String getTemplateName() {
130 | return "Tester TestCase";
131 | }
132 |
133 | @NotNull
134 | @Override
135 | protected String getExtension() {
136 | TesterProjectSettings settings = TesterUtil.getTesterSettings(getProject());
137 | return settings != null ? settings.getDefaultExtension() : "phpt";
138 | }
139 |
140 | @NotNull
141 | @Override
142 | public Properties getProperties(@NotNull PsiDirectory directory) {
143 | Properties properties = super.getProperties(directory);
144 | String fqn = PhpLangUtil.toName(testTargetTextField.getText());
145 | String testedName = PhpLangUtil.toShortName(fqn);
146 |
147 | if (StringUtil.isNotEmpty(testedName)) {
148 | properties.setProperty("TESTED_NAME", testedName);
149 | }
150 |
151 | String namespace = PhpLangUtil.getParentQualifiedName(fqn);
152 | if (!PhpLangUtil.isGlobalNamespaceName(namespace)) {
153 | properties.setProperty("TESTED_NAMESPACE", namespace);
154 | }
155 |
156 | // bootstrap
157 | TesterProjectSettings settings = TesterUtil.getTesterSettings(getProject());
158 | if (settings != null && settings.getBootstrapFile() != null) {
159 | VirtualFile bootstrapFile = LocalFileSystem.getInstance().findFileByPath(settings.getBootstrapFile());
160 | if (bootstrapFile != null && getDirectory() != null) {
161 | Path bootstrapFilePath = Paths.get(bootstrapFile.getPath());
162 | Path testDirectoryPath = Paths.get(directoryComboBox.getSelectedPath());
163 |
164 | Path bootstrapRelativePath = testDirectoryPath.relativize(bootstrapFilePath);
165 | properties.setProperty("BOOTSTRAP_RELATIVE_PATH", bootstrapRelativePath.toString());
166 | }
167 | }
168 |
169 | return properties;
170 | }
171 |
172 | private void createUIComponents() {
173 | testTargetTextField = new EditorTextField("", getProject(), FileTypes.PLAIN_TEXT);
174 | namespaceComboBox = new PhpNamespaceComboBox(getProject(), "", getDisposable());
175 | directoryComboBox = new PhpPsrDirectoryComboBox(getProject()) {
176 | @Override
177 | public void init(@NotNull VirtualFile baseDir, @NotNull String namespace) {
178 | super.init(baseDir, namespace);
179 | ProjectFileIndex index = ProjectRootManager.getInstance(TesterNewTestCaseDialog.this.getProject()).getFileIndex();
180 | this.setDirectoriesFilter(index::isInTestSourceContent);
181 |
182 | this.updateDirectories(TesterNewTestCaseDialog.this.getNamespace());
183 | }
184 | };
185 |
186 | classToTestLabel = new JBLabel(TesterBundle.message("dialog.newTestCase.label.classToTest"));
187 | testClassLabel = new JBLabel(TesterBundle.message("dialog.newTestCase.label.testClass"));
188 | namespaceLabel = new JBLabel(TesterBundle.message("dialog.newTestCase.label.namespace"));
189 | fileNameLabel = new JBLabel(TesterBundle.message("dialog.newTestCase.label.fileName"));
190 | directoryLabel = new JBLabel(TesterBundle.message("dialog.newTestCase.label.directory"));
191 |
192 | testTargetCompletionHint = new JBLabel(UIUtil.ComponentStyle.MINI);
193 | namespaceCompletionHint = new JBLabel(UIUtil.ComponentStyle.MINI);
194 | directoryCompletionHint = new JBLabel(UIUtil.ComponentStyle.MINI);
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/codeGeneration/TesterTestCreator.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.codeGeneration;
2 |
3 | import com.intellij.openapi.editor.Editor;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.openapi.util.Conditions;
6 | import com.intellij.platform.ProjectBaseDirectory;
7 | import com.intellij.psi.PsiFile;
8 | import com.intellij.testIntegration.TestCreator;
9 | import com.jetbrains.php.lang.psi.PhpFile;
10 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
11 | import com.jetbrains.php.lang.psi.elements.PhpClass;
12 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | public class TesterTestCreator implements TestCreator {
16 | public static final TesterTestCreator INSTANCE = new TesterTestCreator();
17 |
18 | private TesterTestCreator() {
19 | }
20 |
21 | @Override
22 | public boolean isAvailable(Project project, Editor editor, PsiFile psiFile) {
23 | PhpClass phpClass = findClass(psiFile);
24 | return phpClass != null;
25 | }
26 |
27 | @Nullable
28 | static PhpClass findClass(PsiFile psiFile) {
29 | if (psiFile instanceof PhpFile) {
30 | PhpClass phpClass = PhpPsiUtil.findClass((PhpFile) psiFile, Conditions.alwaysTrue());
31 | if (phpClass != null && !TesterUtil.isTestClass(phpClass)) {
32 | return phpClass;
33 | }
34 | }
35 |
36 | return null;
37 | }
38 |
39 | @Override
40 | public void createTest(Project project, Editor editor, PsiFile psiFile) {
41 | (new TesterNewTestCaseAction(false)).invoke(project, psiFile.getContainingDirectory(), psiFile, null);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/TesterRunConfiguration.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration;
2 |
3 | import com.intellij.execution.DefaultExecutionResult;
4 | import com.intellij.execution.ExecutionException;
5 | import com.intellij.execution.ExecutionResult;
6 | import com.intellij.execution.Executor;
7 | import com.intellij.execution.configurations.*;
8 | import com.intellij.execution.process.ProcessHandler;
9 | import com.intellij.execution.process.ProcessTerminatedListener;
10 | import com.intellij.execution.runners.ExecutionEnvironment;
11 | import com.intellij.execution.runners.ProgramRunner;
12 | import com.intellij.execution.ui.ConsoleView;
13 | import com.intellij.openapi.options.SettingsEditor;
14 | import com.intellij.openapi.project.Project;
15 | import com.intellij.openapi.util.InvalidDataException;
16 | import com.intellij.openapi.util.WriteExternalException;
17 | import com.intellij.openapi.util.text.StringUtil;
18 | import com.intellij.openapi.vfs.VirtualFile;
19 | import com.intellij.util.PathUtil;
20 | import com.intellij.util.xmlb.annotations.Attribute;
21 | import com.jetbrains.php.config.PhpProjectConfigurationFacade;
22 | import com.jetbrains.php.config.commandLine.PhpCommandLinePathProcessor;
23 | import com.jetbrains.php.config.commandLine.PhpCommandSettings;
24 | import com.jetbrains.php.config.commandLine.PhpCommandSettingsBuilder;
25 | import com.jetbrains.php.config.interpreters.PhpInterpreter;
26 | import com.jetbrains.php.run.PhpCommandLineSettings;
27 | import com.jetbrains.php.run.PhpExecutionUtil;
28 | import com.jetbrains.php.run.PhpRefactoringListenerRunConfiguration;
29 | import com.jetbrains.php.run.PhpRunUtil;
30 | import com.jetbrains.php.util.PhpConfigurationUtil;
31 | import com.jetbrains.php.util.pathmapper.PhpPathMapper;
32 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
33 | import cz.jiripudil.intellij.nette.tester.configuration.editor.TesterRunConfigurationEditor;
34 | import cz.jiripudil.intellij.nette.tester.execution.TesterExecutionUtil;
35 | import cz.jiripudil.intellij.nette.tester.execution.TesterTestLocator;
36 | import org.jdom.Element;
37 | import org.jetbrains.annotations.NotNull;
38 | import org.jetbrains.annotations.Nullable;
39 |
40 | import java.util.Collections;
41 | import java.util.List;
42 | import java.util.Map;
43 |
44 | public class TesterRunConfiguration extends PhpRefactoringListenerRunConfiguration implements LocatableConfiguration {
45 | private boolean isGenerated;
46 |
47 | TesterRunConfiguration(@NotNull Project project, @NotNull ConfigurationFactory factory, String name) {
48 | super(project, factory, name);
49 | }
50 |
51 | @NotNull
52 | @Override
53 | public SettingsEditor extends RunConfiguration> getConfigurationEditor() {
54 | return new TesterRunConfigurationEditor(getProject());
55 | }
56 |
57 | @Override
58 | public void checkConfiguration() throws RuntimeConfigurationException {
59 | TesterSettings settings = getSettings();
60 | PhpInterpreter interpreter = settings.getPhpInterpreter(getProject());
61 | if (interpreter == null) {
62 | interpreter = PhpProjectConfigurationFacade.getInstance(getProject()).getInterpreter();
63 | if (interpreter == null) {
64 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.phpInterpreterNotSet"));
65 |
66 | } else if (interpreter.getPathToPhpExecutable() == null) {
67 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.phpExecutableNotFound"));
68 | }
69 | }
70 |
71 | //TesterSettings settings = getSettings();
72 | VirtualFile scopeDirectory = PhpRunUtil.findDirectory(settings.getTestScope());
73 | VirtualFile scopeFile = PhpRunUtil.findFile(settings.getTestScope());
74 | if (StringUtil.isEmpty(settings.getTestScope())) {
75 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.noTestScope"));
76 |
77 | } else if (scopeDirectory == null && scopeFile == null) {
78 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.testScopeNotFound"));
79 | }
80 |
81 | //PhpInterpreter phpInterpreter = settings.getPhpInterpreter(getProject());
82 | if (interpreter.getPathToPhpExecutable() == null) {
83 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.testEnvExecutableNotFound"));
84 | }
85 |
86 | if (StringUtil.isEmpty(settings.getTesterExecutable())) {
87 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.noExecutable"));
88 |
89 | } else if (PhpRunUtil.findFile(settings.getTesterExecutable()) == null) {
90 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.executableNotFound"));
91 | }
92 |
93 | if (!settings.getUseSystemPhpIni() && StringUtil.isNotEmpty(settings.getPhpIniPath()) && PhpRunUtil.findFile(settings.getPhpIniPath()) == null) {
94 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.phpIniNotFound"));
95 | }
96 |
97 | if (StringUtil.isNotEmpty(settings.getSetupScriptPath()) && PhpRunUtil.findFile(settings.getSetupScriptPath()) == null) {
98 | throw new RuntimeConfigurationError(TesterBundle.message("runConfiguration.errors.setupScriptNotFound"));
99 | }
100 |
101 | PhpRunUtil.checkCommandLineSettings(getProject(), settings.getPhpCommandLineSettings());
102 | }
103 |
104 | @Nullable
105 | @Override
106 | public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException {
107 | try {
108 | return this.getState(executionEnvironment, this.createCommand(Collections.emptyMap(), Collections.emptyList(), false));
109 | } catch (CloneNotSupportedException e) {
110 | return null;
111 | }
112 | }
113 |
114 | @NotNull
115 | private RunProfileState getState(@NotNull final ExecutionEnvironment executionEnvironment, @NotNull final PhpCommandSettings command) throws ExecutionException {
116 | try {
117 | this.checkConfiguration();
118 | } catch (RuntimeConfigurationException e) {
119 | throw new ExecutionException(e.getMessage() + " for " + this.getName() + " run-configuration");
120 | }
121 |
122 | return new CommandLineState(executionEnvironment) {
123 | @NotNull
124 | @Override
125 | protected ProcessHandler startProcess() throws ExecutionException {
126 | ProcessHandler processHandler = createProcessHandler(getProject(), command);
127 | PhpRunUtil.attachProcessOutputDebugDumper(processHandler);
128 | ProcessTerminatedListener.attach(processHandler, getProject());
129 | return processHandler;
130 | }
131 |
132 | @NotNull
133 | public ExecutionResult execute(@NotNull Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
134 | ProcessHandler processHandler = this.startProcess();
135 |
136 | PhpPathMapper pathMapper = command.getPathProcessor().createPathMapper(getProject());
137 | TesterTestLocator locationProvider = TesterTestLocator.create(pathMapper);
138 | ConsoleView consoleView = TesterExecutionUtil.createConsole(getProject(), processHandler, executionEnvironment, locationProvider);
139 | PhpExecutionUtil.addMessageFilters(getProject(), consoleView, locationProvider.getPathMapper());
140 | return new DefaultExecutionResult(consoleView, processHandler);
141 | }
142 | };
143 | }
144 |
145 | @NotNull
146 | private PhpCommandSettings createCommand(Map envParameters, List arguments, boolean withDebuggerOptions) throws ExecutionException, CloneNotSupportedException {
147 | PhpInterpreter interpreter = PhpProjectConfigurationFacade.getInstance(getProject()).getInterpreter();
148 | if (interpreter == null) {
149 | throw new ExecutionException(TesterBundle.message("runConfiguration.errors.phpInterpreterNotSet"));
150 |
151 | } else {
152 | TesterSettings settings = getSettings().clone();
153 |
154 | PhpCommandSettings command = PhpCommandSettingsBuilder.create(getProject(), interpreter, withDebuggerOptions);
155 |
156 | processSettings(command.getPathProcessor(), settings);
157 |
158 | command.setScript(settings.getTesterExecutable());
159 |
160 | PhpCommandLineSettings commandLineSettings = settings.getPhpCommandLineSettings();
161 | command.importCommandLineSettings(commandLineSettings, null);
162 | command.addEnvs(envParameters);
163 |
164 | // support for user setup
165 | if (settings.getSetupScriptPath() != null) {
166 | command.addEnv("INTELLIJ_NETTE_TESTER_USER_SETUP", command.getPathProcessor().process(settings.getSetupScriptPath()));
167 | }
168 |
169 | TesterExecutionUtil.addCommandArguments(getProject(), command, settings, arguments);
170 |
171 | return command;
172 | }
173 | }
174 |
175 | @NotNull
176 | @Override
177 | protected TesterSettings createSettings() {
178 | return new TesterSettings();
179 | }
180 |
181 | protected void processSettings(@NotNull PhpCommandLinePathProcessor processor, @NotNull TesterSettings settings) {
182 | settings.setTestScope(processor.process(settings.getTestScope()));
183 | settings.setPhpIniPath(processor.process(StringUtil.notNullize(settings.getPhpIniPath())));
184 | settings.setSetupScriptPath(processor.process(StringUtil.notNullize(settings.getSetupScriptPath())));
185 | }
186 |
187 | @Override
188 | protected void fixSettingsAfterDeserialization(@NotNull TesterSettings settings) {
189 | settings.setTesterExecutable(PhpConfigurationUtil.deserializePath(settings.getTesterExecutable()));
190 | settings.setTestScope(PhpConfigurationUtil.deserializePath(settings.getTestScope()));
191 | settings.setPhpIniPath(PhpConfigurationUtil.deserializePath(settings.getPhpIniPath()));
192 | settings.setSetupScriptPath(PhpConfigurationUtil.deserializePath(settings.getSetupScriptPath()));
193 | }
194 |
195 | @Override
196 | protected void fixSettingsBeforeSerialization(@NotNull TesterSettings settings) {
197 | settings.setTesterExecutable(PhpConfigurationUtil.serializePath(settings.getTesterExecutable()));
198 | settings.setTestScope(PhpConfigurationUtil.serializePath(settings.getTestScope()));
199 | settings.setPhpIniPath(PhpConfigurationUtil.serializePath(settings.getPhpIniPath()));
200 | settings.setSetupScriptPath(PhpConfigurationUtil.serializePath(settings.getSetupScriptPath()));
201 | }
202 |
203 | @Override
204 | public void readExternal(Element element) throws InvalidDataException {
205 | super.readExternal(element);
206 |
207 | if (!isNewSerializationUsed()) {
208 | isGenerated = "true".equals(element.getAttributeValue("isGeneratedName"));
209 | }
210 | }
211 |
212 | @Override
213 | public void writeExternal(@NotNull Element element) throws WriteExternalException {
214 | if (!isNewSerializationUsed() && isGeneratedName()) {
215 | element.setAttribute("isGeneratedName", "true");
216 | }
217 |
218 | super.writeExternal(element);
219 | }
220 |
221 | @Attribute("isGeneratedName")
222 | @Override
223 | public boolean isGeneratedName() {
224 | return isGenerated && PhpRunUtil.isGeneratedName(this);
225 | }
226 |
227 | void setGeneratedName(@Nullable String name) {
228 | setName(name);
229 | isGenerated = true;
230 | }
231 |
232 | @Nullable
233 | @Override
234 | public String suggestedName() {
235 | return PathUtil.getFileName(getSettings().getTestScope());
236 | }
237 |
238 | @NotNull
239 | @Override
240 | protected List> getPathsToUpdate() {
241 | List> pathsToUpdate = super.getPathsToUpdate();
242 |
243 | pathsToUpdate.add(new PhpRefValue() {
244 | @NotNull
245 | @Override
246 | public String getValue() {
247 | return TesterRunConfiguration.this.getSettings().getTestScope();
248 | }
249 |
250 | @Override
251 | public void setValue(@Nullable String newName) {
252 | TesterRunConfiguration.this.getSettings().setTestScope(newName);
253 | }
254 | });
255 |
256 | pathsToUpdate.add(new PhpRefValue() {
257 | @NotNull
258 | @Override
259 | public String getValue() {
260 | return TesterRunConfiguration.this.getSettings().getTesterExecutable();
261 | }
262 |
263 | @Override
264 | public void setValue(@Nullable String newName) {
265 | TesterRunConfiguration.this.getSettings().setTesterExecutable(newName);
266 | }
267 | });
268 |
269 | pathsToUpdate.add(new PhpRefValue() {
270 | @Nullable
271 | @Override
272 | public String getValue() {
273 | return TesterRunConfiguration.this.getSettings().getPhpIniPath();
274 | }
275 |
276 | @Override
277 | public void setValue(@Nullable String newName) {
278 | TesterRunConfiguration.this.getSettings().setPhpIniPath(newName);
279 | }
280 | });
281 |
282 | pathsToUpdate.add(new PhpRefValue() {
283 | @Nullable
284 | @Override
285 | public String getValue() {
286 | return TesterRunConfiguration.this.getSettings().getSetupScriptPath();
287 | }
288 |
289 | @Override
290 | public void setValue(@Nullable String newName) {
291 | TesterRunConfiguration.this.getSettings().setSetupScriptPath(newName);
292 | }
293 | });
294 |
295 | return pathsToUpdate;
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/TesterRunConfigurationProducer.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration;
2 |
3 | import com.intellij.execution.actions.ConfigurationContext;
4 | import com.intellij.execution.actions.RunConfigurationProducer;
5 | import com.intellij.openapi.util.Ref;
6 | import com.intellij.psi.PsiDirectory;
7 | import com.intellij.psi.PsiElement;
8 | import com.jetbrains.php.lang.psi.PhpFile;
9 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
10 | import com.jetbrains.php.lang.psi.elements.PhpClass;
11 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
12 |
13 | public class TesterRunConfigurationProducer extends RunConfigurationProducer {
14 | protected TesterRunConfigurationProducer() {
15 | super(TesterRunConfigurationType.getInstance());
16 | }
17 |
18 | @Override
19 | protected boolean setupConfigurationFromContext(TesterRunConfiguration runConfiguration, ConfigurationContext configurationContext, Ref ref) {
20 | PsiElement location = configurationContext.getPsiLocation();
21 | if (location instanceof PsiDirectory) {
22 | PsiDirectory directory = (PsiDirectory) location;
23 | runConfiguration.getSettings().setTestScope(directory.getVirtualFile().getPresentableUrl());
24 | runConfiguration.setGeneratedName(runConfiguration.suggestedName());
25 | ref.set(directory);
26 | return true;
27 | }
28 |
29 | if (location instanceof PhpFile) {
30 | PhpFile phpFile = (PhpFile) location;
31 | PhpClass testClass = PhpPsiUtil.findClass(phpFile, TesterUtil::isTestClass);
32 | if (testClass == null) {
33 | return false;
34 | }
35 |
36 | runConfiguration.getSettings().setTestScope(phpFile.getVirtualFile().getPresentableUrl());
37 | runConfiguration.setGeneratedName(runConfiguration.suggestedName());
38 | ref.set(phpFile);
39 | return true;
40 | }
41 |
42 | return false;
43 | }
44 |
45 | @Override
46 | public boolean isConfigurationFromContext(TesterRunConfiguration runConfiguration, ConfigurationContext configurationContext) {
47 | PsiElement location = configurationContext.getPsiLocation();
48 | if (location instanceof PsiDirectory) {
49 | PsiDirectory directory = (PsiDirectory) location;
50 | return runConfiguration.getSettings().getTestScope().equals(directory.getVirtualFile().getPresentableUrl());
51 | }
52 |
53 | if (location instanceof PhpFile) {
54 | PhpFile file = (PhpFile) location;
55 | return runConfiguration.getSettings().getTestScope().equals(file.getVirtualFile().getPresentableUrl());
56 | }
57 |
58 | return false;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/TesterRunConfigurationType.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration;
2 |
3 | import com.intellij.execution.configurations.ConfigurationFactory;
4 | import com.intellij.execution.configurations.ConfigurationTypeBase;
5 | import com.intellij.execution.configurations.ConfigurationTypeUtil;
6 | import com.intellij.execution.configurations.RunConfiguration;
7 | import com.intellij.openapi.project.Project;
8 | import com.jetbrains.php.PhpIcons;
9 | import com.jetbrains.php.run.PhpRunConfigurationFactoryBase;
10 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | public class TesterRunConfigurationType extends ConfigurationTypeBase {
14 | protected TesterRunConfigurationType() {
15 | super("nette-tester", TesterBundle.message("configurationType.displayName"), TesterBundle.message("configurationType.description"), PhpIcons.PHP_TEST_FILE);
16 | this.addFactory(createFactory(this));
17 | }
18 |
19 | static TesterRunConfigurationType getInstance() {
20 | return ConfigurationTypeUtil.findConfigurationType(TesterRunConfigurationType.class);
21 | }
22 |
23 | public static ConfigurationFactory createFactory(TesterRunConfigurationType type) {
24 | return new PhpRunConfigurationFactoryBase(type) {
25 | @NotNull
26 | @Override
27 | public RunConfiguration createTemplateConfiguration(@NotNull Project project) {
28 | return new TesterRunConfiguration(project, this, this.getName());
29 | }
30 |
31 | @Override
32 | public String getName() {
33 | return TesterBundle.message("configurationType.displayName");
34 | }
35 | };
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/TesterSettings.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.text.StringUtil;
5 | import com.intellij.util.xmlb.annotations.Property;
6 | import com.intellij.util.xmlb.annotations.Transient;
7 | import com.jetbrains.php.config.PhpProjectConfigurationFacade;
8 | import com.jetbrains.php.config.interpreters.PhpConfigurationOptionData;
9 | import com.jetbrains.php.config.interpreters.PhpInterpreter;
10 | import com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl;
11 | import com.jetbrains.php.run.PhpCommandLineSettings;
12 | import com.jetbrains.php.run.PhpRunConfigurationSettings;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | public class TesterSettings implements PhpRunConfigurationSettings, Cloneable {
20 | @Nullable private String testScope;
21 | @Nullable private String testerExecutable;
22 | @Nullable private String testerOptions;
23 | @Nullable private String setupScriptPath;
24 |
25 | @Nullable private String phpInterpreterId;
26 | @NotNull private List phpInterpreterOptions = new ArrayList<>();
27 | @Nullable private String phpIniPath;
28 | @NotNull private Boolean useSystemPhpIni = Boolean.FALSE;
29 |
30 | @NotNull private PhpCommandLineSettings phpCommandLineSettings = new PhpCommandLineSettings();
31 |
32 | @Property
33 | @NotNull
34 | public String getTestScope() {
35 | return StringUtil.notNullize(testScope);
36 | }
37 |
38 | public void setTestScope(@Nullable String testScope) {
39 | this.testScope = testScope;
40 | }
41 |
42 | @Property
43 | @NotNull
44 | public String getTesterExecutable() {
45 | return StringUtil.notNullize(testerExecutable);
46 | }
47 |
48 | public void setTesterExecutable(@Nullable String testerExecutable) {
49 | this.testerExecutable = testerExecutable;
50 | }
51 |
52 | @Property
53 | @Nullable
54 | public String getTesterOptions() {
55 | return testerOptions;
56 | }
57 |
58 | public void setTesterOptions(@Nullable String testerOptions) {
59 | this.testerOptions = testerOptions;
60 | }
61 |
62 | @Property
63 | @Nullable
64 | public String getSetupScriptPath() {
65 | return setupScriptPath;
66 | }
67 |
68 | public void setSetupScriptPath(@Nullable String setupScriptPath) {
69 | this.setupScriptPath = setupScriptPath;
70 | }
71 |
72 | @Property
73 | @Nullable
74 | public String getPhpInterpreterId() {
75 | return phpInterpreterId;
76 | }
77 |
78 | @Transient
79 | @Nullable
80 | public PhpInterpreter getPhpInterpreter(@NotNull Project project) {
81 | return phpInterpreterId != null
82 | ? PhpInterpretersManagerImpl.getInstance(project).findInterpreterById(phpInterpreterId)
83 | : PhpProjectConfigurationFacade.getInstance(project).getInterpreter();
84 | }
85 |
86 | public void setPhpInterpreterId(@Nullable String phpInterpreterId) {
87 | this.phpInterpreterId = phpInterpreterId;
88 | }
89 |
90 | @Property
91 | @NotNull
92 | public List getPhpInterpreterOptions() {
93 | return phpInterpreterOptions;
94 | }
95 |
96 | public void setPhpInterpreterOptions(@NotNull List phpInterpreterOptions) {
97 | this.phpInterpreterOptions = phpInterpreterOptions;
98 | }
99 |
100 | @Property
101 | @Nullable
102 | public String getPhpIniPath() {
103 | return phpIniPath;
104 | }
105 |
106 | public void setPhpIniPath(@Nullable String phpIniPath) {
107 | this.phpIniPath = phpIniPath;
108 | }
109 |
110 | @Property
111 | @NotNull
112 | public Boolean getUseSystemPhpIni() {
113 | return useSystemPhpIni;
114 | }
115 |
116 | public void setUseSystemPhpIni(@NotNull Boolean useSystemPhpIni) {
117 | this.useSystemPhpIni = useSystemPhpIni;
118 | }
119 |
120 | @Property
121 | @NotNull
122 | public PhpCommandLineSettings getPhpCommandLineSettings() {
123 | return phpCommandLineSettings;
124 | }
125 |
126 | @Nullable
127 | @Override
128 | public String getWorkingDirectory() {
129 | return phpCommandLineSettings.getWorkingDirectory();
130 | }
131 |
132 | @Override
133 | public void setWorkingDirectory(@Nullable String workingDirectory) {
134 | phpCommandLineSettings.setWorkingDirectory(workingDirectory);
135 | }
136 |
137 | @Override
138 | public boolean equals(Object o) {
139 | if (this == o) return true;
140 | if (!(o instanceof TesterSettings)) return false;
141 |
142 | TesterSettings that = (TesterSettings) o;
143 |
144 | if (testScope != null ? !testScope.equals(that.testScope) : that.testScope != null) return false;
145 | if (testerExecutable != null ? !testerExecutable.equals(that.testerExecutable) : that.testerExecutable != null)
146 | return false;
147 | if (testerOptions != null ? !testerOptions.equals(that.testerOptions) : that.testerOptions != null)
148 | return false;
149 | if (phpIniPath != null ? !phpIniPath.equals(that.phpIniPath) : that.phpIniPath != null) return false;
150 | if (setupScriptPath != null ? !setupScriptPath.equals(that.setupScriptPath) : that.setupScriptPath != null) return false;
151 |
152 | return phpCommandLineSettings.equals(that.getPhpCommandLineSettings());
153 | }
154 |
155 | @Override
156 | public int hashCode() {
157 | int result = testScope != null ? testScope.hashCode() : 0;
158 | result = 31 * result + (testerExecutable != null ? testerExecutable.hashCode() : 0);
159 | result = 31 * result + (testerOptions != null ? testerOptions.hashCode() : 0);
160 | result = 31 * result + (phpIniPath != null ? phpIniPath.hashCode() : 0);
161 | result = 31 * result + (setupScriptPath != null ? setupScriptPath.hashCode() : 0);
162 | result = 31 * result + phpCommandLineSettings.hashCode();
163 | return result;
164 | }
165 |
166 | @Override
167 | public TesterSettings clone() throws CloneNotSupportedException {
168 | return (TesterSettings) super.clone();
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/TesterTestMethodRunConfiguration.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration;
2 |
3 | import com.intellij.execution.configurations.ConfigurationFactory;
4 | import com.intellij.openapi.project.Project;
5 | import com.jetbrains.php.PhpIcons;
6 | import com.jetbrains.php.lang.psi.elements.Method;
7 | import com.jetbrains.php.run.script.PhpScriptRunConfiguration;
8 | import org.jetbrains.annotations.NotNull;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import javax.swing.*;
12 |
13 | public class TesterTestMethodRunConfiguration extends PhpScriptRunConfiguration {
14 | public TesterTestMethodRunConfiguration(Project project, ConfigurationFactory factory, String name) {
15 | super(project, factory, name);
16 | }
17 |
18 | @Override
19 | public Icon getIcon() {
20 | return PhpIcons.PHP_TEST_METHOD;
21 | }
22 |
23 | @Override
24 | public String suggestedName() {
25 | return super.suggestedName() + createSuggestedName(parseMethod());
26 | }
27 |
28 | public static String createSuggestedName(@Nullable String methodName) {
29 | return methodName != null ? " " + methodName + "()" : "";
30 | }
31 |
32 | void setMethod(@NotNull Method method) {
33 | setMethodName(method.getName());
34 | }
35 |
36 | public void setMethodName(@NotNull String methodName) {
37 | getSettings().setScriptParameters(methodName);
38 | }
39 |
40 | boolean isMethod(@NotNull Method method) {
41 | return method.getName().equals(parseMethod());
42 | }
43 |
44 | @Nullable
45 | public String getMethod() {
46 | return parseMethod();
47 | }
48 |
49 | @Nullable
50 | public String parseMethod() {
51 | return getSettings().getScriptParameters();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/TesterTestMethodRunConfigurationProducer.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration;
2 |
3 | import com.intellij.execution.actions.ConfigurationContext;
4 | import com.intellij.execution.actions.RunConfigurationProducer;
5 | import com.intellij.ide.scratch.ScratchFileType;
6 | import com.intellij.openapi.fileTypes.FileTypeManager;
7 | import com.intellij.openapi.roots.ProjectRootManager;
8 | import com.intellij.openapi.util.Ref;
9 | import com.intellij.openapi.util.text.StringUtil;
10 | import com.intellij.openapi.vfs.LocalFileSystem;
11 | import com.intellij.openapi.vfs.VirtualFile;
12 | import com.intellij.psi.PsiElement;
13 | import com.intellij.psi.PsiFile;
14 | import com.jetbrains.php.lang.PhpFileType;
15 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
16 | import com.jetbrains.php.lang.psi.elements.Method;
17 | import com.jetbrains.php.run.script.PhpScriptRunConfiguration;
18 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | public class TesterTestMethodRunConfigurationProducer extends RunConfigurationProducer {
22 | protected TesterTestMethodRunConfigurationProducer() {
23 | super(TesterTestMethodRunConfigurationType.getInstance());
24 | }
25 |
26 | @Override
27 | protected boolean setupConfigurationFromContext(TesterTestMethodRunConfiguration runConfiguration, ConfigurationContext context, Ref ref) {
28 | PsiElement element = context.getPsiLocation();
29 | Method method = PhpPsiUtil.getParentByCondition(element, parent -> parent instanceof Method);
30 |
31 | if (method != null && isValid(method)) {
32 | VirtualFile file = method.getContainingFile().getVirtualFile();
33 | ref.set(method);
34 |
35 | if (!FileTypeManager.getInstance().isFileOfType(file, ScratchFileType.INSTANCE)) {
36 | VirtualFile root = ProjectRootManager.getInstance(element.getProject()).getFileIndex().getContentRootForFile(file);
37 | if (root == null) {
38 | return false;
39 | }
40 | }
41 |
42 | PhpScriptRunConfiguration.Settings settings = runConfiguration.getSettings();
43 | settings.setPath(file.getPresentableUrl());
44 | runConfiguration.setMethod(method);
45 | runConfiguration.setName(runConfiguration.suggestedName());
46 | return true;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | @Override
53 | public boolean isConfigurationFromContext(TesterTestMethodRunConfiguration runConfiguration, ConfigurationContext context) {
54 | PsiElement element = context.getPsiLocation();
55 | Method method = PhpPsiUtil.getParentByCondition(element, parent -> parent instanceof Method);
56 |
57 | if (method != null && isValid(method)) {
58 | VirtualFile containingVirtualFile = method.getContainingFile().getVirtualFile();
59 | PhpScriptRunConfiguration.Settings settings = runConfiguration.getSettings();
60 | String path = settings.getPath();
61 |
62 | if (path != null) {
63 | VirtualFile configurationFile = LocalFileSystem.getInstance().findFileByPath(path);
64 | if (configurationFile != null) {
65 | return StringUtil.equals(containingVirtualFile.getPath(), configurationFile.getPath())
66 | && runConfiguration.isMethod(method);
67 | }
68 | }
69 | }
70 |
71 | return false;
72 | }
73 |
74 | private boolean isValid(@Nullable Method method) {
75 | return method != null
76 | && isValid(method.getContainingFile())
77 | && TesterUtil.isTestMethod(method);
78 | }
79 |
80 | private boolean isValid(@Nullable PsiFile containingFile) {
81 | return containingFile != null
82 | && containingFile.getFileType() == PhpFileType.INSTANCE
83 | && containingFile.getVirtualFile() != null;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/TesterTestMethodRunConfigurationType.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration;
2 |
3 | import com.intellij.execution.configurations.ConfigurationFactory;
4 | import com.intellij.execution.configurations.ConfigurationTypeBase;
5 | import com.intellij.execution.configurations.ConfigurationTypeUtil;
6 | import com.intellij.execution.configurations.RunConfiguration;
7 | import com.intellij.openapi.project.Project;
8 | import com.jetbrains.php.PhpIcons;
9 | import com.jetbrains.php.run.PhpRunConfigurationFactoryBase;
10 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | public class TesterTestMethodRunConfigurationType extends ConfigurationTypeBase {
14 | protected TesterTestMethodRunConfigurationType() {
15 | super("nette-tester-method", TesterBundle.message("configurationType.method.displayName"), TesterBundle.message("configurationType.method.description"), PhpIcons.PHP_TEST_METHOD);
16 | this.addFactory(createFactory(this));
17 | }
18 |
19 | static TesterTestMethodRunConfigurationType getInstance() {
20 | return ConfigurationTypeUtil.findConfigurationType(TesterTestMethodRunConfigurationType.class);
21 | }
22 |
23 | public static ConfigurationFactory createFactory() {
24 | return createFactory(getInstance());
25 | }
26 |
27 | public static ConfigurationFactory createFactory(TesterTestMethodRunConfigurationType type) {
28 | return new PhpRunConfigurationFactoryBase(type) {
29 | @NotNull
30 | @Override
31 | public RunConfiguration createTemplateConfiguration(@NotNull Project project) {
32 | return new TesterTestMethodRunConfiguration(project, this, "");
33 | }
34 | };
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/PhpCommandLineSettingsEditor.form:
--------------------------------------------------------------------------------
1 |
2 |
56 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/PhpCommandLineSettingsEditor.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration.editor;
2 |
3 | import com.intellij.execution.configuration.EnvironmentVariablesComponent;
4 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
5 | import com.intellij.openapi.options.ConfigurationException;
6 | import com.intellij.openapi.options.SettingsEditor;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.openapi.ui.TextFieldWithBrowseButton;
9 | import com.intellij.ui.RawCommandLineEditor;
10 | import com.intellij.ui.components.JBLabel;
11 | import com.jetbrains.php.run.PhpCommandLineSettings;
12 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
13 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import javax.swing.*;
17 |
18 | public class PhpCommandLineSettingsEditor extends SettingsEditor {
19 | @NotNull final private Project project;
20 |
21 | private JPanel panel;
22 | private RawCommandLineEditor interpreterOptions;
23 | private TextFieldWithBrowseButton customWorkingDirectory;
24 | private EnvironmentVariablesComponent environmentVariables;
25 |
26 | private JBLabel interpreterOptionsLabel;
27 | private JBLabel workingDirectoryLabel;
28 |
29 | PhpCommandLineSettingsEditor(@NotNull final Project project) {
30 | this.project = project;
31 | }
32 |
33 | private void createUIComponents() {
34 | interpreterOptions = new RawCommandLineEditor();
35 | interpreterOptions.setDialogCaption("Options");
36 |
37 | customWorkingDirectory = new TextFieldWithBrowseButton();
38 | customWorkingDirectory.addBrowseFolderListener(null, null, project, FileChooserDescriptorFactory.createSingleFolderDescriptor());
39 |
40 | environmentVariables = new EnvironmentVariablesComponent();
41 |
42 | interpreterOptionsLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.cli.interpreterOptions"));
43 | workingDirectoryLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.cli.workingDirectory"));
44 | }
45 |
46 | @Override
47 | protected void resetEditorFrom(@NotNull TesterRunConfiguration runConfiguration) {
48 | PhpCommandLineSettings commandLineSettings = runConfiguration.getSettings().getPhpCommandLineSettings();
49 | interpreterOptions.setText(commandLineSettings.getParameters());
50 | customWorkingDirectory.setText(commandLineSettings.getWorkingDirectory());
51 | environmentVariables.setEnvs(commandLineSettings.getEnvs());
52 | environmentVariables.setPassParentEnvs(commandLineSettings.isPassParentEnvs());
53 | }
54 |
55 | @Override
56 | protected void applyEditorTo(@NotNull TesterRunConfiguration runConfiguration) throws ConfigurationException {
57 | PhpCommandLineSettings commandLineSettings = runConfiguration.getSettings().getPhpCommandLineSettings();
58 | commandLineSettings.setParameters(interpreterOptions.getText());
59 | commandLineSettings.setWorkingDirectory(customWorkingDirectory.getText());
60 | commandLineSettings.setEnvs(environmentVariables.getEnvs());
61 | commandLineSettings.setPassParentEnvs(environmentVariables.isPassParentEnvs());
62 | }
63 |
64 | @NotNull
65 | @Override
66 | protected JComponent createEditor() {
67 | return panel;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/TesterRunConfigurationEditor.form:
--------------------------------------------------------------------------------
1 |
2 |
74 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/TesterRunConfigurationEditor.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration.editor;
2 |
3 | import com.intellij.openapi.options.ConfigurationException;
4 | import com.intellij.openapi.options.SettingsEditor;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.ui.JBColor;
7 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
8 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import javax.swing.*;
12 |
13 | public class TesterRunConfigurationEditor extends SettingsEditor {
14 | @NotNull private final Project project;
15 |
16 | private JPanel panel;
17 |
18 | private JPanel testerSettingsPanel;
19 | private TesterSettingsEditor testerSettingsEditor;
20 |
21 | private JPanel testEnvironmentSettingsPanel;
22 | private TesterTestEnvironmentSettingsEditor testEnvironmentSettingsEditor;
23 |
24 | private JPanel cliSettingsPanel;
25 | private PhpCommandLineSettingsEditor phpCommandLineSettingsEditor;
26 |
27 | public TesterRunConfigurationEditor(@NotNull final Project project) {
28 | super();
29 | this.project = project;
30 | }
31 |
32 | @Override
33 | protected void resetEditorFrom(@NotNull TesterRunConfiguration runConfiguration) {
34 | testerSettingsEditor.resetEditorFrom(runConfiguration);
35 | testEnvironmentSettingsEditor.resetEditorFrom(runConfiguration);
36 | phpCommandLineSettingsEditor.resetEditorFrom(runConfiguration);
37 | }
38 |
39 | @Override
40 | protected void applyEditorTo(@NotNull TesterRunConfiguration runConfiguration) throws ConfigurationException {
41 | testerSettingsEditor.applyEditorTo(runConfiguration);
42 | testEnvironmentSettingsEditor.applyEditorTo(runConfiguration);
43 | phpCommandLineSettingsEditor.applyEditorTo(runConfiguration);
44 | }
45 |
46 | @NotNull
47 | @Override
48 | protected JComponent createEditor() {
49 | return panel;
50 | }
51 |
52 | private void createUIComponents() {
53 | testerSettingsPanel = new JPanel();
54 | testerSettingsPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, JBColor.LIGHT_GRAY), TesterBundle.message("runConfiguration.editor.tester.title")));
55 | testerSettingsEditor = new TesterSettingsEditor(project);
56 |
57 | testEnvironmentSettingsPanel = new JPanel();
58 | testEnvironmentSettingsPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, JBColor.LIGHT_GRAY), TesterBundle.message("runConfiguration.editor.testEnv.title")));
59 | testEnvironmentSettingsEditor = new TesterTestEnvironmentSettingsEditor(project);
60 |
61 | cliSettingsPanel = new JPanel();
62 | cliSettingsPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, JBColor.LIGHT_GRAY), TesterBundle.message("runConfiguration.editor.cli.title")));
63 | phpCommandLineSettingsEditor = new PhpCommandLineSettingsEditor(project);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/TesterSettingsEditor.form:
--------------------------------------------------------------------------------
1 |
2 |
77 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/TesterSettingsEditor.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration.editor;
2 |
3 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
4 | import com.intellij.openapi.options.ConfigurationException;
5 | import com.intellij.openapi.options.SettingsEditor;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.ui.TextFieldWithBrowseButton;
8 | import com.intellij.ui.RawCommandLineEditor;
9 | import com.intellij.ui.components.JBLabel;
10 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
11 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
12 | import cz.jiripudil.intellij.nette.tester.configuration.TesterSettings;
13 | import org.jetbrains.annotations.NotNull;
14 |
15 | import javax.swing.*;
16 |
17 | public class TesterSettingsEditor extends SettingsEditor {
18 | @NotNull private final Project project;
19 |
20 | private JPanel panel;
21 | private TextFieldWithBrowseButton testScope;
22 | private TextFieldWithBrowseButton testerExecutable;
23 | private RawCommandLineEditor testerOptions;
24 | private TextFieldWithBrowseButton userSetupScript;
25 |
26 | private JBLabel testScopeLabel;
27 | private JBLabel testerExecutableLabel;
28 | private JBLabel testerOptionsLabel;
29 | private JBLabel setupScriptLabel;
30 |
31 | TesterSettingsEditor(@NotNull final Project project) {
32 | this.project = project;
33 | }
34 |
35 | @Override
36 | protected void resetEditorFrom(@NotNull TesterRunConfiguration runConfiguration) {
37 | TesterSettings settings = runConfiguration.getSettings();
38 | testScope.setText(settings.getTestScope());
39 | testerExecutable.setText(settings.getTesterExecutable());
40 | testerOptions.setText(settings.getTesterOptions());
41 | userSetupScript.setText(settings.getSetupScriptPath());
42 | }
43 |
44 | @Override
45 | protected void applyEditorTo(@NotNull TesterRunConfiguration runConfiguration) throws ConfigurationException {
46 | TesterSettings settings = runConfiguration.getSettings();
47 | settings.setTestScope(testScope.getText());
48 | settings.setTesterExecutable(testerExecutable.getText());
49 | settings.setTesterOptions(testerOptions.getText());
50 | settings.setSetupScriptPath(userSetupScript.getText());
51 | }
52 |
53 | @NotNull
54 | @Override
55 | protected JComponent createEditor() {
56 | return panel;
57 | }
58 |
59 | private void createUIComponents() {
60 | testScope = new TextFieldWithBrowseButton();
61 | testScope.addBrowseFolderListener(null, null, project, FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor());
62 |
63 | testerExecutable = new TextFieldWithBrowseButton();
64 | testerExecutable.addBrowseFolderListener(null, null, project, FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor());
65 |
66 | testerOptions = new RawCommandLineEditor();
67 | testerOptions.setDialogCaption("Options");
68 |
69 | userSetupScript = new TextFieldWithBrowseButton();
70 | userSetupScript.addBrowseFolderListener(null, null, project, FileChooserDescriptorFactory.createSingleFileDescriptor("php"));
71 |
72 | testScopeLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.tester.testScope"));
73 | testerExecutableLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.tester.testerExecutable"));
74 | testerOptionsLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.tester.testerOptions"));
75 | setupScriptLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.tester.setupScript"));
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/TesterTestEnvironmentSettingsEditor.form:
--------------------------------------------------------------------------------
1 |
2 |
75 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/configuration/editor/TesterTestEnvironmentSettingsEditor.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.configuration.editor;
2 |
3 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
4 | import com.intellij.openapi.options.ConfigurationException;
5 | import com.intellij.openapi.options.SettingsEditor;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.ui.TextFieldWithBrowseButton;
8 | import com.intellij.ui.components.JBCheckBox;
9 | import com.intellij.ui.components.JBLabel;
10 | import com.jetbrains.php.config.PhpProjectConfigurationFacade;
11 | import com.jetbrains.php.config.interpreters.PhpConfigurationOptionsComponent;
12 | import com.jetbrains.php.config.interpreters.PhpInterpreter;
13 | import com.jetbrains.php.config.interpreters.PhpInterpreterComboBox;
14 | import com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl;
15 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
16 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
17 | import cz.jiripudil.intellij.nette.tester.configuration.TesterSettings;
18 | import org.jetbrains.annotations.NotNull;
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | import javax.swing.*;
22 |
23 | public class TesterTestEnvironmentSettingsEditor extends SettingsEditor {
24 | @NotNull private final Project project;
25 |
26 | private JPanel panel;
27 | private PhpInterpreterComboBox phpInterpreter;
28 | private PhpConfigurationOptionsComponent interpreterOptions;
29 | private TextFieldWithBrowseButton phpIniPath;
30 | private JBCheckBox useSystemPhpIniCheckbox;
31 |
32 | private JBLabel interpreterLabel;
33 | private JBLabel interpreterOptionsLabel;
34 | private JBLabel pathToPhpIniLabel;
35 |
36 | TesterTestEnvironmentSettingsEditor(@NotNull final Project project) {
37 | super();
38 | this.project = project;
39 | useSystemPhpIniCheckbox.addItemListener(e -> phpIniPath.setEnabled(!useSystemPhpIniCheckbox.isSelected()));
40 | }
41 |
42 | @Override
43 | protected void resetEditorFrom(@NotNull TesterRunConfiguration runConfiguration) {
44 | TesterSettings settings = runConfiguration.getSettings();
45 |
46 | PhpInterpreter interpreter = settings.getPhpInterpreter(project);
47 | phpInterpreter.reset(PhpInterpretersManagerImpl.getInstance(project).findInterpreterName(interpreter != null ? interpreter.getId() : null));
48 | interpreterOptions.setConfigurationOptions(settings.getPhpInterpreterOptions());
49 | phpIniPath.setText(settings.getPhpIniPath());
50 | useSystemPhpIniCheckbox.setSelected(settings.getUseSystemPhpIni());
51 | }
52 |
53 | @Override
54 | protected void applyEditorTo(@NotNull TesterRunConfiguration runConfiguration) throws ConfigurationException {
55 | TesterSettings settings = runConfiguration.getSettings();
56 |
57 | PhpInterpreter interpreter = getSelectedInterpreter();
58 | settings.setPhpInterpreterId(interpreter != null ? interpreter.getId() : null);
59 | settings.setPhpInterpreterOptions(interpreterOptions.getConfigurationOptionsData());
60 | settings.setPhpIniPath(phpIniPath.getText());
61 | settings.setUseSystemPhpIni(useSystemPhpIniCheckbox.isSelected());
62 | }
63 |
64 | @Nullable
65 | private PhpInterpreter getSelectedInterpreter() {
66 | String interpreterName = phpInterpreter.getSelectedItemName();
67 | if (interpreterName == null) {
68 | return PhpProjectConfigurationFacade.getInstance(project).getInterpreter();
69 | }
70 |
71 | return PhpInterpretersManagerImpl.getInstance(project).findInterpreter(interpreterName);
72 | }
73 |
74 | @NotNull
75 | @Override
76 | protected JComponent createEditor() {
77 | return panel;
78 | }
79 |
80 | private void createUIComponents() {
81 | phpInterpreter = new PhpInterpreterComboBox(project, null);
82 | phpInterpreter.setModel(PhpInterpretersManagerImpl.getInstance(project).getInterpreters(), null);
83 | phpInterpreter.setNoItemText("");
84 |
85 | interpreterOptions = new PhpConfigurationOptionsComponent();
86 |
87 | phpIniPath = new TextFieldWithBrowseButton();
88 | phpIniPath.addBrowseFolderListener(null, null, project, FileChooserDescriptorFactory.createSingleFileDescriptor("ini"));
89 |
90 | useSystemPhpIniCheckbox = new JBCheckBox(TesterBundle.message("runConfiguration.editor.testEnv.useSystemPhpIni"));
91 |
92 | interpreterLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.testEnv.interpreter"));
93 | interpreterOptionsLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.testEnv.interpreterOptions"));
94 | pathToPhpIniLabel = new JBLabel(TesterBundle.message("runConfiguration.editor.testEnv.phpIni"));
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/execution/TesterConsoleProperties.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.execution;
2 |
3 | import com.intellij.execution.Executor;
4 | import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
5 | import com.intellij.execution.testframework.sm.runner.SMTestLocator;
6 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | public class TesterConsoleProperties extends SMTRunnerConsoleProperties {
11 | private final SMTestLocator locator;
12 |
13 | TesterConsoleProperties(@NotNull TesterRunConfiguration config, Executor executor, SMTestLocator locator) {
14 | super(config, "Nette Tester", executor);
15 | this.locator = locator;
16 | }
17 |
18 | @Nullable
19 | @Override
20 | public SMTestLocator getTestLocator() {
21 | return locator;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/execution/TesterExecutionUtil.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.execution;
2 |
3 | import com.intellij.execution.ExecutionException;
4 | import com.intellij.execution.process.ProcessHandler;
5 | import com.intellij.execution.runners.ExecutionEnvironment;
6 | import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
7 | import com.intellij.execution.testframework.ui.BaseTestsOutputConsoleView;
8 | import com.intellij.execution.ui.ConsoleView;
9 | import com.intellij.openapi.application.PathManager;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.openapi.util.Disposer;
12 | import com.intellij.openapi.util.text.StringUtil;
13 | import com.jetbrains.php.config.commandLine.PhpCommandSettings;
14 | import com.jetbrains.php.config.interpreters.PhpConfigurationOptionData;
15 | import com.jetbrains.php.config.interpreters.PhpInterpreter;
16 | import com.jetbrains.php.run.filters.XdebugCallStackFilter;
17 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
18 | import cz.jiripudil.intellij.nette.tester.configuration.TesterRunConfiguration;
19 | import cz.jiripudil.intellij.nette.tester.configuration.TesterSettings;
20 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterProjectSettings;
21 | import org.jetbrains.annotations.NotNull;
22 |
23 | import java.io.IOException;
24 | import java.io.InputStream;
25 | import java.nio.file.Files;
26 | import java.nio.file.Path;
27 | import java.nio.file.Paths;
28 | import java.nio.file.StandardCopyOption;
29 | import java.util.Arrays;
30 | import java.util.List;
31 |
32 | public class TesterExecutionUtil {
33 | public static void addCommandArguments(@NotNull Project project, @NotNull PhpCommandSettings command, TesterSettings settings, List arguments) throws ExecutionException {
34 | PhpInterpreter testEnvironmentInterpreter = settings.getPhpInterpreter(project);
35 | if (testEnvironmentInterpreter != null && testEnvironmentInterpreter.getPathToPhpExecutable() != null) {
36 | command.addArgument("-p");
37 | command.addArgument(testEnvironmentInterpreter.getPathToPhpExecutable());
38 | }
39 |
40 | if (settings.getUseSystemPhpIni()) {
41 | command.addArgument("-C");
42 |
43 | } else if (!StringUtil.isEmpty(settings.getPhpIniPath())) {
44 | command.addArgument("-c");
45 | command.addArgument(settings.getPhpIniPath());
46 | }
47 |
48 | try {
49 | Path tempDir = getTempPath(project);
50 | if (!Files.isDirectory(tempDir)) {
51 | Files.createDirectory(tempDir);
52 | }
53 |
54 | TesterProjectSettings testerSettings = TesterUtil.getTesterSettings(project);
55 | String setupFile = testerSettings.getSetupFile();
56 |
57 | Path setupScriptPath = Paths.get(tempDir.toString(), setupFile);
58 | InputStream setupResourceStream = TesterExecutionUtil.class.getClassLoader().getResourceAsStream(setupFile);
59 | if (setupResourceStream == null) {
60 | throw new ExecutionException("Input stream can not be null");
61 | }
62 | Files.copy(setupResourceStream, setupScriptPath, StandardCopyOption.REPLACE_EXISTING);
63 | setupResourceStream.close();
64 |
65 | command.addArgument("--setup");
66 | command.addArgument(command.getPathProcessor().process(setupScriptPath.toString()));
67 |
68 | } catch (IOException e) {
69 | throw new ExecutionException(e);
70 | }
71 |
72 | if (!StringUtil.isEmpty(settings.getTesterOptions())) {
73 | String[] optionsArray = settings.getTesterOptions().split(" ");
74 | command.addArguments(Arrays.asList(optionsArray));
75 | }
76 |
77 | for (PhpConfigurationOptionData configurationOption : settings.getPhpInterpreterOptions()) {
78 | command.addArgument("-d");
79 | command.addArgument(configurationOption.getName() + (!configurationOption.getValue().isEmpty() ? "=" + configurationOption.getValue() : ""));
80 | }
81 |
82 | command.addArguments(arguments);
83 | command.addArgument(settings.getTestScope());
84 | }
85 |
86 | private static Path getTempPath(Project project) {
87 | if (project.getBasePath() != null) {
88 | return Paths.get(project.getBasePath(), ".idea", "intellij-nette-tester");
89 | } else {
90 | return Paths.get(PathManager.getPluginsPath(), "intellij-nette-tester");
91 | }
92 | }
93 |
94 | public static ConsoleView createConsole(Project project, ProcessHandler processHandler, ExecutionEnvironment executionEnvironment, TesterTestLocator locationProvider) {
95 | TesterRunConfiguration profile = (TesterRunConfiguration) executionEnvironment.getRunProfile();
96 |
97 | TesterConsoleProperties properties = new TesterConsoleProperties(profile, executionEnvironment.getExecutor(), locationProvider);
98 | properties.addStackTraceFilter(new XdebugCallStackFilter(project, locationProvider.getPathMapper()));
99 |
100 | BaseTestsOutputConsoleView testsOutputConsoleView = SMTestRunnerConnectionUtil.createConsole("Nette Tester", properties);
101 | testsOutputConsoleView.addMessageFilter(new TesterStackTraceFilter(project, locationProvider.getPathMapper()));
102 | testsOutputConsoleView.attachToProcess(processHandler);
103 | Disposer.register(project, testsOutputConsoleView);
104 | return testsOutputConsoleView;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/execution/TesterStackTraceFilter.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.execution;
2 |
3 | import com.intellij.openapi.diagnostic.Logger;
4 | import com.intellij.openapi.progress.ProcessCanceledException;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.text.StringUtil;
7 | import com.jetbrains.php.run.filters.PhpFilter;
8 | import com.jetbrains.php.util.pathmapper.PhpPathMapper;
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import java.util.regex.Matcher;
13 | import java.util.regex.Pattern;
14 |
15 |
16 | public class TesterStackTraceFilter extends PhpFilter {
17 | private static final Logger LOG = Logger.getInstance("#cz.jiripudil.intellij.nette.tester.execution.TesterStackTraceFilter");
18 | private static final Pattern STACK_TRACE_PATTERN;
19 |
20 | TesterStackTraceFilter(@NotNull Project project, @NotNull PhpPathMapper pathMapper) {
21 | super(project, pathMapper);
22 | }
23 |
24 | @Nullable
25 | @Override
26 | public MyResult applyFilter(@NotNull String line) {
27 | return apply(STACK_TRACE_PATTERN, line);
28 | }
29 |
30 | private MyResult apply(Pattern pattern, String line) {
31 | try {
32 | Matcher matcher = pattern.matcher(StringUtil.newBombedCharSequence(line, 1000L));
33 | if (matcher.find()) {
34 | String fileName = matcher.group(1);
35 | String lineNumberString = matcher.group(2);
36 | if (fileName == null || lineNumberString == null) {
37 | return null;
38 | }
39 |
40 | try {
41 | int lineNumber = Integer.parseInt(lineNumberString);
42 | return new MyResult(fileName, lineNumber, matcher.start(1), matcher.end(1));
43 | } catch (NumberFormatException var6) {
44 | // ignore
45 | }
46 | }
47 |
48 | return null;
49 | } catch (ProcessCanceledException var7) {
50 | LOG.warn("Matching took too long for line: " + line);
51 | return null;
52 | }
53 | }
54 |
55 | static {
56 | STACK_TRACE_PATTERN = Pattern.compile("in\\s(" + WHOLE_FILENAME_PATTERN + ")\\((\\p{Digit}*)\\)");
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/execution/TesterTestLocator.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.execution;
2 |
3 | import com.intellij.execution.Location;
4 | import com.intellij.execution.PsiLocation;
5 | import com.intellij.execution.testframework.sm.runner.SMTestLocator;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.vfs.VirtualFile;
8 | import com.intellij.psi.PsiElement;
9 | import com.intellij.psi.PsiFile;
10 | import com.intellij.psi.PsiManager;
11 | import com.intellij.psi.search.GlobalSearchScope;
12 | import com.intellij.psi.stubs.StubIndex;
13 | import com.jetbrains.php.lang.psi.PhpFile;
14 | import com.jetbrains.php.lang.psi.elements.Method;
15 | import com.jetbrains.php.lang.psi.stubs.indexes.PhpMethodIndex;
16 | import com.jetbrains.php.util.pathmapper.PhpPathMapper;
17 | import org.jetbrains.annotations.NotNull;
18 | import org.jetbrains.annotations.Nullable;
19 |
20 | import java.util.Collection;
21 | import java.util.Collections;
22 | import java.util.List;
23 |
24 | public class TesterTestLocator implements SMTestLocator {
25 | private static final String PROTOCOL_FILE = "tester_file";
26 | private static final String PROTOCOL_METHOD = "tester_method";
27 |
28 | @NotNull
29 | private final PhpPathMapper pathMapper;
30 |
31 | private TesterTestLocator(@NotNull PhpPathMapper pathMapper) {
32 | this.pathMapper = pathMapper;
33 | }
34 |
35 | public static TesterTestLocator create(@NotNull PhpPathMapper pathMapper) {
36 | return new TesterTestLocator(pathMapper);
37 | }
38 |
39 | @NotNull
40 | public PhpPathMapper getPathMapper() {
41 | return pathMapper;
42 | }
43 |
44 | @NotNull
45 | @Override
46 | public List getLocation(@NotNull String protocol, @NotNull String path, @NotNull Project project, @NotNull GlobalSearchScope globalSearchScope) {
47 | if (protocol.equals(PROTOCOL_FILE)) {
48 | PsiFile element = findFile(path, project);
49 | if (element != null) {
50 | return Collections.singletonList(new PsiLocation<>(project, element));
51 | }
52 |
53 | } else if (protocol.equals(PROTOCOL_METHOD)) {
54 | Method element = findMethod(path, project);
55 | if (element != null) {
56 | return Collections.singletonList(new PsiLocation<>(project, element));
57 | }
58 | }
59 |
60 | return Collections.emptyList();
61 | }
62 |
63 | @Nullable
64 | private PsiFile findFile(String path, Project project) {
65 | VirtualFile virtualFile = this.pathMapper.getLocalFile(path);
66 | if (virtualFile == null) {
67 | return null;
68 | }
69 |
70 | PsiFile result = PsiManager.getInstance(project).findFile(virtualFile);
71 | if (!(result instanceof PhpFile)) {
72 | return null;
73 | }
74 |
75 | return result;
76 | }
77 |
78 | @Nullable
79 | private Method findMethod(String path, Project project) {
80 | String[] location = path.split("#");
81 | int tokensNumber = location.length;
82 | if (tokensNumber == 2) {
83 | String filePath = location[0];
84 | String methodName = location[1];
85 |
86 | if (filePath == null) {
87 | return null;
88 |
89 | } else {
90 | PsiFile file = findFile(filePath, project);
91 | if (file == null) {
92 | return null;
93 | }
94 |
95 | GlobalSearchScope scope = GlobalSearchScope.fileScope(project, file.getVirtualFile());
96 | Collection methods = StubIndex.getElements(PhpMethodIndex.KEY, methodName, project, scope, Method.class);
97 | if (methods.size() == 1) {
98 | return methods.iterator().next();
99 | }
100 | }
101 | }
102 |
103 | return null;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/inspections/TestCaseAnnotationInspection.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.inspections;
2 |
3 | import com.intellij.codeInspection.*;
4 | import com.intellij.openapi.editor.Document;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.TextRange;
7 | import com.intellij.psi.*;
8 | import com.intellij.psi.codeStyle.CodeStyleManager;
9 | import com.intellij.psi.util.PsiTreeUtil;
10 | import com.jetbrains.php.lang.documentation.phpdoc.lexer.PhpDocTokenTypes;
11 | import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
12 | import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
13 | import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
14 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
15 | import com.jetbrains.php.lang.psi.elements.PhpClass;
16 | import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor;
17 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
18 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
19 | import org.jetbrains.annotations.Nls;
20 | import org.jetbrains.annotations.NotNull;
21 |
22 | public class TestCaseAnnotationInspection extends LocalInspectionTool {
23 | private static final AnnotateTestCaseQuickFix QUICK_FIX = new AnnotateTestCaseQuickFix();
24 |
25 | @NotNull
26 | @Override
27 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
28 | return new PhpElementVisitor() {
29 | @Override
30 | public void visitPhpClass(PhpClass phpClass) {
31 | if ( ! TesterUtil.isTestClass(phpClass)) {
32 | return;
33 | }
34 |
35 | PhpDocComment docComment = phpClass.getDocComment();
36 | if (phpClass.getIdentifyingElement() != null && (docComment == null || docComment.getTagElementsByName("@testCase").length == 0)) {
37 | holder.registerProblem(phpClass.getIdentifyingElement(), TesterBundle.message("inspections.annotation.description"), QUICK_FIX);
38 | }
39 | }
40 | };
41 | }
42 |
43 | private static class AnnotateTestCaseQuickFix implements LocalQuickFix {
44 | @Nls
45 | @NotNull
46 | @Override
47 | public String getFamilyName() {
48 | return TesterBundle.message("inspections.familyName");
49 | }
50 |
51 | @Nls
52 | @NotNull
53 | @Override
54 | public String getName() {
55 | return TesterBundle.message("inspections.annotation.quickFix");
56 | }
57 |
58 | @Override
59 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor problemDescriptor) {
60 | PhpClass phpClass = (PhpClass) problemDescriptor.getPsiElement();
61 | PsiFile containingFile = phpClass.getContainingFile();
62 |
63 | PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
64 | Document document = manager.getDocument(containingFile);
65 |
66 | String template = "/**\n* @testCase\n*/";
67 | PsiElement testCaseTag = PhpPsiElementFactory.createFromText(project, PhpDocTag.class, template);
68 | assert testCaseTag != null;
69 |
70 | PhpDocComment docComment = phpClass.getDocComment();
71 | if (docComment == null) {
72 | PsiElement testCaseDocComment = PhpPsiElementFactory.createFromText(project, PhpDocComment.class, template);
73 | assert testCaseDocComment != null;
74 | phpClass.getParent().addBefore(testCaseDocComment, phpClass);
75 |
76 | if (document != null) {
77 | manager.doPostponedOperationsAndUnblockDocument(document);
78 | TextRange reformatRange = testCaseDocComment.getTextRange();
79 | CodeStyleManager.getInstance(project).reformatText(containingFile, reformatRange.getStartOffset(), reformatRange.getEndOffset());
80 | }
81 |
82 | } else {
83 | PsiElement star = PhpPsiElementFactory.createFromText(project, PhpDocTokenTypes.DOC_LEADING_ASTERISK, template);
84 | PsiElement lineFeed = star.getPrevSibling();
85 | PsiElement insertAfter = PhpPsiUtil.getChildOfType(docComment, PhpDocTokenTypes.DOC_COMMENT_END);
86 |
87 | if (insertAfter != null) {
88 | insertAfter = insertAfter.getPrevSibling();
89 | int offset = insertAfter.getTextRange().getStartOffset();
90 | PsiElement insertedLF = null;
91 | if (insertAfter instanceof PsiWhiteSpace && ! insertAfter.textContains('\n') && lineFeed != null) {
92 | insertedLF = docComment.addAfter(lineFeed, insertAfter);
93 | }
94 |
95 | testCaseTag = docComment.addAfter(testCaseTag, insertedLF == null ? insertAfter : insertedLF);
96 | docComment.addBefore(star, testCaseTag);
97 | if (document != null) {
98 | manager.doPostponedOperationsAndUnblockDocument(document);
99 | PhpDocComment updatedComment = PsiTreeUtil.getParentOfType(containingFile.findElementAt(offset), PhpDocComment.class);
100 | if (updatedComment != null) {
101 | TextRange reformatRange = updatedComment.getTextRange();
102 | CodeStyleManager.getInstance(project).reformatText(containingFile, reformatRange.getStartOffset(), reformatRange.getEndOffset());
103 | }
104 | }
105 | }
106 | }
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/inspections/TestCaseIsRunInspection.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.inspections;
2 |
3 | import com.intellij.codeInspection.*;
4 | import com.intellij.openapi.editor.Document;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.TextRange;
7 | import com.intellij.psi.PsiDocumentManager;
8 | import com.intellij.psi.PsiElement;
9 | import com.intellij.psi.PsiElementVisitor;
10 | import com.intellij.psi.PsiFile;
11 | import com.intellij.psi.codeStyle.CodeStyleManager;
12 | import com.intellij.psi.impl.source.tree.LeafPsiElement;
13 | import com.jetbrains.php.lang.psi.PhpFile;
14 | import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
15 | import com.jetbrains.php.lang.psi.elements.ExtendsList;
16 | import com.jetbrains.php.lang.psi.elements.MethodReference;
17 | import com.jetbrains.php.lang.psi.elements.PhpClass;
18 | import com.jetbrains.php.lang.psi.elements.Statement;
19 | import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor;
20 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
21 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
22 | import org.jetbrains.annotations.Nls;
23 | import org.jetbrains.annotations.NotNull;
24 |
25 | import java.util.ArrayList;
26 | import java.util.HashSet;
27 |
28 | public class TestCaseIsRunInspection extends LocalInspectionTool {
29 | private static final AddRunMethodCallQuickFix ADD_RUN_METHOD_CALL_QUICK_FIX = new AddRunMethodCallQuickFix();
30 | private static final MakeAbstractQuickFix MAKE_ABSTRACT_QUICK_FIX = new MakeAbstractQuickFix();
31 |
32 | private ArrayList testClasses;
33 | private HashSet runReferencedClassNames;
34 |
35 | @NotNull
36 | @Override
37 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
38 | return new PhpElementVisitor() {
39 | @Override
40 | public void visitPhpClass(PhpClass phpClass) {
41 | if (TesterUtil.isTestClass(phpClass)) {
42 | testClasses.add(phpClass);
43 | }
44 | }
45 |
46 | @Override
47 | public void visitPhpMethodReference(MethodReference reference) {
48 | if (reference.getClassReference() == null) {
49 | return;
50 | }
51 |
52 | if (reference.getCanonicalText().equalsIgnoreCase("run") || reference.getCanonicalText().equalsIgnoreCase("runTest")) {
53 | runReferencedClassNames.add(reference.getClassReference().getType().toString());
54 | }
55 | }
56 | };
57 | }
58 |
59 | @Override
60 | public void inspectionStarted(@NotNull LocalInspectionToolSession session, boolean isOnTheFly) {
61 | if ( ! (session.getFile() instanceof PhpFile)) {
62 | return;
63 | }
64 |
65 | testClasses = new ArrayList<>();
66 | runReferencedClassNames = new HashSet<>();
67 | }
68 |
69 | @Override
70 | public void inspectionFinished(@NotNull LocalInspectionToolSession session, @NotNull ProblemsHolder problemsHolder) {
71 | if ( ! (session.getFile() instanceof PhpFile)) {
72 | return;
73 | }
74 |
75 | testClasses.forEach((PhpClass testClass) -> {
76 | if ( ! runReferencedClassNames.contains(testClass.getFQN())) {
77 | ExtendsList extendsList = testClass.getExtendsList();
78 | TextRange highlightRange = new TextRange(0, extendsList.getStartOffsetInParent() + extendsList.getTextLength());
79 | problemsHolder.registerProblem(
80 | testClass, TesterBundle.message("inspections.runTestCase.description"), ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
81 | highlightRange, ADD_RUN_METHOD_CALL_QUICK_FIX, ! testClass.isFinal() ? MAKE_ABSTRACT_QUICK_FIX : null
82 | );
83 | }
84 | });
85 | }
86 |
87 | private static class AddRunMethodCallQuickFix implements LocalQuickFix {
88 | @Nls
89 | @NotNull
90 | @Override
91 | public String getFamilyName() {
92 | return TesterBundle.message("inspections.familyName");
93 | }
94 |
95 | @Nls
96 | @NotNull
97 | @Override
98 | public String getName() {
99 | return TesterBundle.message("inspections.runTestCase.addRunMethodCall.quickFix");
100 | }
101 |
102 | @Override
103 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor problemDescriptor) {
104 | PsiElement element = problemDescriptor.getPsiElement();
105 | if ( ! (element instanceof PhpClass)) {
106 | return;
107 | }
108 |
109 | PsiFile containingFile = element.getContainingFile();
110 | PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
111 | Document document = manager.getDocument(containingFile);
112 |
113 | PhpClass phpClass = (PhpClass) element;
114 | String template = "\n\n(new " + phpClass.getName() + "())->run();";
115 | Statement runMethodCall = PhpPsiElementFactory.createStatement(project, template);
116 |
117 | phpClass.getParent().addAfter(runMethodCall, phpClass);
118 |
119 | if (document != null) {
120 | manager.doPostponedOperationsAndUnblockDocument(document);
121 | TextRange reformatRange = runMethodCall.getTextRange();
122 | CodeStyleManager.getInstance(project).reformatText(containingFile, reformatRange.getStartOffset(), reformatRange.getEndOffset());
123 | }
124 | }
125 | }
126 |
127 | private static class MakeAbstractQuickFix implements LocalQuickFix {
128 | @Nls
129 | @NotNull
130 | @Override
131 | public String getFamilyName() {
132 | return TesterBundle.message("inspections.familyName");
133 | }
134 |
135 | @Nls
136 | @NotNull
137 | @Override
138 | public String getName() {
139 | return TesterBundle.message("inspections.runTestCase.makeAbstract.quickFix");
140 | }
141 |
142 | @Override
143 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor problemDescriptor) {
144 | PsiElement element = problemDescriptor.getPsiElement();
145 | if ( ! (element instanceof PhpClass)) {
146 | return;
147 | }
148 |
149 | PsiFile containingFile = element.getContainingFile();
150 | PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
151 | Document document = manager.getDocument(containingFile);
152 |
153 | PhpClass phpClass = (PhpClass) element;
154 | PsiElement abstractKeyword = PhpPsiElementFactory.createFromText(project, LeafPsiElement.class, "abstract");
155 | if (abstractKeyword == null) {
156 | return;
157 | }
158 |
159 | phpClass.addBefore(abstractKeyword, phpClass.getFirstChild());
160 |
161 | if (document != null) {
162 | manager.doPostponedOperationsAndUnblockDocument(document);
163 | TextRange reformatRange = abstractKeyword.getTextRange();
164 | CodeStyleManager.getInstance(project).reformatText(containingFile, reformatRange.getStartOffset(), reformatRange.getEndOffset());
165 | }
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/inspections/TestFileNameInspection.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.inspections;
2 |
3 | import com.intellij.codeInspection.*;
4 | import com.intellij.openapi.diagnostic.Logger;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.text.StringUtil;
7 | import com.intellij.openapi.vfs.VirtualFile;
8 | import com.intellij.psi.PsiElement;
9 | import com.intellij.psi.PsiElementVisitor;
10 | import com.jetbrains.php.lang.psi.PhpFile;
11 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
12 | import com.jetbrains.php.lang.psi.elements.PhpClass;
13 | import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor;
14 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
15 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
16 | import org.jetbrains.annotations.Nls;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | import java.io.IOException;
20 |
21 | public class TestFileNameInspection extends LocalInspectionTool {
22 | private static final Logger LOG = Logger.getInstance("#cz.jiripudil.intellij.nette.tester.inspections.TestFileNameInspection");
23 | private static final ChangeExtensionToPhptQuickFix CHANGE_EXTENSION_TO_PHPT_QUICK_FIX = new ChangeExtensionToPhptQuickFix();
24 | private static final AddTestSuffixQuickFix ADD_TEST_SUFFIX_QUICK_FIX = new AddTestSuffixQuickFix();
25 |
26 | @NotNull
27 | @Override
28 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
29 | return new PhpElementVisitor() {
30 | @Override
31 | public void visitPhpFile(PhpFile phpFile) {
32 | PhpClass testClass = PhpPsiUtil.findClass(phpFile, TesterUtil::isTestClass);
33 | if (testClass != null && ! hasValidName(phpFile.getVirtualFile())) {
34 | holder.registerProblem(phpFile, TesterBundle.message("inspections.fileName.description"), CHANGE_EXTENSION_TO_PHPT_QUICK_FIX, ADD_TEST_SUFFIX_QUICK_FIX);
35 | }
36 | }
37 | };
38 | }
39 |
40 | private boolean hasValidName(VirtualFile file) {
41 | return "phpt".equals(file.getExtension())
42 | || ("php".equals(file.getExtension()) && StringUtil.endsWith(file.getNameWithoutExtension(), "Test"));
43 | }
44 |
45 | private static class ChangeExtensionToPhptQuickFix implements LocalQuickFix {
46 | @Nls
47 | @NotNull
48 | @Override
49 | public String getFamilyName() {
50 | return TesterBundle.message("inspections.familyName");
51 | }
52 |
53 | @Nls
54 | @NotNull
55 | @Override
56 | public String getName() {
57 | return TesterBundle.message("inspections.fileName.quickFix.phpt");
58 | }
59 |
60 | @Override
61 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor problemDescriptor) {
62 | PsiElement element = problemDescriptor.getPsiElement();
63 | if (element instanceof PhpFile) {
64 | PhpFile phpFile = (PhpFile) element;
65 | VirtualFile virtualFile = phpFile.getVirtualFile();
66 | String newName = virtualFile.getNameWithoutExtension() + ".phpt";
67 |
68 | try {
69 | virtualFile.rename(this, newName);
70 |
71 | } catch (IOException e) {
72 | LOG.error(e);
73 | }
74 | }
75 | }
76 | }
77 |
78 | private static class AddTestSuffixQuickFix implements LocalQuickFix {
79 | @Nls
80 | @NotNull
81 | @Override
82 | public String getFamilyName() {
83 | return TesterBundle.message("inspections.familyName");
84 | }
85 |
86 | @Nls
87 | @NotNull
88 | @Override
89 | public String getName() {
90 | return TesterBundle.message("inspections.fileName.quickFix.test");
91 | }
92 |
93 | @Override
94 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor problemDescriptor) {
95 | PsiElement element = problemDescriptor.getPsiElement();
96 | if (element instanceof PhpFile) {
97 | PhpFile phpFile = (PhpFile) element;
98 | VirtualFile virtualFile = phpFile.getVirtualFile();
99 | String oldName = virtualFile.getNameWithoutExtension();
100 | String newName = oldName + "Test";
101 |
102 | PhpClass phpClass = PhpPsiUtil.findClass(phpFile, checkedClass -> checkedClass.getName().equals(oldName));
103 | if (phpClass != null) {
104 | phpClass.setName(newName);
105 | }
106 |
107 | try {
108 | virtualFile.rename(this, newName + "." + virtualFile.getExtension());
109 |
110 | } catch (IOException e) {
111 | LOG.error(e);
112 | }
113 | }
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/lineMarker/TesterMethodRunLineMarkerProvider.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.lineMarker;
2 |
3 | import com.intellij.execution.lineMarker.RunLineMarkerContributor;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.psi.PsiElement;
6 | import com.jetbrains.php.lang.lexer.PhpTokenTypes;
7 | import com.jetbrains.php.lang.psi.elements.Method;
8 | import com.jetbrains.php.lang.psi.elements.PhpClass;
9 | import cz.jiripudil.intellij.nette.tester.TesterIcons;
10 | import cz.jiripudil.intellij.nette.tester.action.TesterCreateMethodRunTestAction;
11 | import cz.jiripudil.intellij.nette.tester.action.TesterMethodRunTestAction;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | public class TesterMethodRunLineMarkerProvider extends RunLineMarkerContributor {
16 | static String TEST_METHOD_PREFIX = "test";
17 |
18 | @Override
19 | public @Nullable Info getInfo(@NotNull PsiElement psiElement) {
20 | if (psiElement.getNode().getElementType() == PhpTokenTypes.IDENTIFIER) {
21 | PsiElement method = psiElement.getParent();
22 | if (method instanceof Method) {
23 | String methodName = ((Method) method).getName();
24 | if (!methodName.startsWith(TEST_METHOD_PREFIX)) {
25 | return null;
26 | }
27 |
28 | PhpClass phpClass = ((Method) method).getContainingClass();
29 | String className = phpClass != null ? phpClass.getName() : "null";
30 |
31 | if (psiElement.getText().equals(methodName)) {
32 | AnAction[] actions = new AnAction[2];
33 | actions[0] = new TesterMethodRunTestAction(psiElement, className, methodName);
34 | actions[1] = new TesterCreateMethodRunTestAction(psiElement, className, methodName);
35 |
36 | return new Info(TesterIcons.RUN_METHOD, actions, RunLineMarkerContributor.RUN_TEST_TOOLTIP_PROVIDER);
37 | }
38 | }
39 | }
40 | return null;
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/lineMarker/TesterRunLineMarkerProvider.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.lineMarker;
2 |
3 | import com.intellij.execution.lineMarker.RunLineMarkerContributor;
4 | import com.intellij.openapi.actionSystem.AnAction;
5 | import com.intellij.psi.PsiElement;
6 | import com.jetbrains.php.lang.lexer.PhpTokenTypes;
7 | import com.jetbrains.php.lang.psi.elements.PhpClass;
8 | import cz.jiripudil.intellij.nette.tester.TesterIcons;
9 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
10 | import cz.jiripudil.intellij.nette.tester.action.TesterCreateRunTestAction;
11 | import cz.jiripudil.intellij.nette.tester.action.TesterRunTestAction;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | public class TesterRunLineMarkerProvider extends RunLineMarkerContributor {
16 | @Override
17 | public @Nullable Info getInfo(@NotNull PsiElement psiElement) {
18 | if (psiElement.getNode().getElementType() == PhpTokenTypes.IDENTIFIER) {
19 | PsiElement phpClass = psiElement.getParent();
20 | if (!(phpClass instanceof PhpClass) || !TesterUtil.isTestClass((PhpClass) phpClass)) {
21 | return null;
22 | }
23 |
24 | String className = psiElement.getText();
25 | if (((PhpClass) phpClass).getName().equals(className)) {
26 | AnAction[] actions = new AnAction[2];
27 | actions[0] = new TesterRunTestAction(psiElement, className);
28 | actions[1] = new TesterCreateRunTestAction(psiElement, className);
29 |
30 | return new Info(TesterIcons.RUN_CLASS, actions, RunLineMarkerContributor.RUN_TEST_TOOLTIP_PROVIDER);
31 | }
32 | }
33 | return null;
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/TesterConfigurable.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.projectSettings;
2 |
3 | import com.intellij.openapi.options.Configurable;
4 | import com.intellij.openapi.options.ConfigurationException;
5 | import com.intellij.openapi.options.SearchableConfigurable;
6 | import com.intellij.openapi.project.Project;
7 | import cz.jiripudil.intellij.nette.tester.projectSettings.editor.TesterConfigurableForm;
8 | import org.jetbrains.annotations.Nls;
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import javax.swing.*;
13 |
14 | public class TesterConfigurable implements SearchableConfigurable, Configurable.NoScroll {
15 | private final Project project;
16 | private TesterConfigurableForm form;
17 |
18 | public TesterConfigurable(@NotNull final Project project) {
19 | this.project = project;
20 | }
21 |
22 | @NotNull
23 | @Override
24 | public String getId() {
25 | return this.getClass().getName();
26 | }
27 |
28 | @Nls
29 | @Override
30 | public String getDisplayName() {
31 | return "Nette Tester";
32 | }
33 |
34 | @Nullable
35 | @Override
36 | public JComponent createComponent() {
37 | if (form == null) {
38 | form = new TesterConfigurableForm(project);
39 | }
40 |
41 | return form.getComponent();
42 | }
43 |
44 | @Override
45 | public boolean isModified() {
46 | return form.isModified();
47 | }
48 |
49 | @Override
50 | public void apply() throws ConfigurationException {
51 | form.apply();
52 | }
53 |
54 | @Override
55 | public void reset() {
56 | form.reset();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/TesterNamespaceMapping.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.projectSettings;
2 |
3 | import com.intellij.util.xmlb.annotations.Attribute;
4 | import com.intellij.util.xmlb.annotations.Tag;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | @Tag("testNamespaceMapping")
8 | public class TesterNamespaceMapping {
9 | @NotNull private String sourceNamespace;
10 | @NotNull private String testsNamespace;
11 |
12 | private TesterNamespaceMapping(@NotNull String sourceNamespace, @NotNull String testsNamespace) {
13 | this.sourceNamespace = sourceNamespace;
14 | this.testsNamespace = testsNamespace;
15 | }
16 |
17 | public TesterNamespaceMapping() {
18 | this("", "");
19 | }
20 |
21 | @Attribute("sourceNamespace")
22 | @NotNull
23 | public String getSourceNamespace() {
24 | return sourceNamespace;
25 | }
26 |
27 | public void setSourceNamespace(@NotNull String sourceNamespace) {
28 | this.sourceNamespace = sourceNamespace;
29 | }
30 |
31 | @Attribute("testsNamespace")
32 | @NotNull
33 | public String getTestsNamespace() {
34 | return testsNamespace;
35 | }
36 |
37 | public void setTestsNamespace(@NotNull String testsNamespace) {
38 | this.testsNamespace = testsNamespace;
39 | }
40 |
41 | @Override
42 | public boolean equals(Object other) {
43 | if (this == other) return true;
44 | if (!(other instanceof TesterNamespaceMapping)) return false;
45 |
46 | TesterNamespaceMapping that = (TesterNamespaceMapping) other;
47 |
48 | return sourceNamespace.equals(that.sourceNamespace)
49 | && testsNamespace.equals(that.testsNamespace);
50 | }
51 |
52 | @Override
53 | public int hashCode() {
54 | int result = sourceNamespace.hashCode();
55 | result = 31 * result + testsNamespace.hashCode();
56 | return result;
57 | }
58 |
59 | @Override
60 | public TesterNamespaceMapping clone() {
61 | return new TesterNamespaceMapping(
62 | this.sourceNamespace,
63 | this.testsNamespace
64 | );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/TesterProjectSettings.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.projectSettings;
2 |
3 | import com.intellij.util.xmlb.annotations.Attribute;
4 | import com.intellij.util.xmlb.annotations.Tag;
5 | import com.jetbrains.php.util.PhpConfigurationUtil;
6 | import org.jetbrains.annotations.NotNull;
7 | import org.jetbrains.annotations.Nullable;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | @Tag("testerSettings")
13 | public class TesterProjectSettings {
14 | @NotNull private String defaultExtension = "phpt";
15 | @NotNull private String testerVersion = ">= 2.0";
16 | @Nullable private String bootstrapFile;
17 | @NotNull private List namespaceMappings = new ArrayList<>();
18 |
19 | @Attribute("defaultExtension")
20 | @NotNull
21 | public String getDefaultExtension() {
22 | return defaultExtension;
23 | }
24 |
25 | @Attribute("testerVersion")
26 | @NotNull
27 | public String getTesterVersion() {
28 | return testerVersion;
29 | }
30 |
31 | public String getSetupFile() {
32 | return testerVersion.equals("< 2.0") ? "setup.php" : "setup2-0.php";
33 | }
34 |
35 | public void setTesterVersion(@NotNull String defaultExtension) {
36 | this.testerVersion = defaultExtension;
37 | }
38 |
39 | public void setDefaultExtension(@NotNull String defaultExtension) {
40 | this.defaultExtension = defaultExtension;
41 | }
42 |
43 | @Attribute("bootstrapFile")
44 | @Nullable
45 | public String getBootstrapFile() {
46 | return PhpConfigurationUtil.deserializePath(bootstrapFile);
47 | }
48 |
49 | public void setBootstrapFile(@Nullable String bootstrapFile) {
50 | this.bootstrapFile = PhpConfigurationUtil.serializePath(bootstrapFile);
51 | }
52 |
53 | @Tag("namespaceMappings")
54 | @NotNull
55 | public List getNamespaceMappings() {
56 | return namespaceMappings;
57 | }
58 |
59 | public void setNamespaceMappings(@NotNull List namespaceMappings) {
60 | this.namespaceMappings = namespaceMappings;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/TesterProjectSettingsManager.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.projectSettings;
2 |
3 | import com.intellij.openapi.components.*;
4 | import com.intellij.openapi.project.Project;
5 | import org.jetbrains.annotations.NotNull;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | @State(
9 | name = "Tester",
10 | storages = {@Storage("tester.xml")}
11 | )
12 | public class TesterProjectSettingsManager implements PersistentStateComponent {
13 | private TesterProjectSettings settings;
14 |
15 | public TesterProjectSettingsManager() {
16 | this.settings = new TesterProjectSettings();
17 | }
18 |
19 | @NotNull
20 | public static TesterProjectSettingsManager getInstance(@NotNull Project project) {
21 | return ServiceManager.getService(project, TesterProjectSettingsManager.class);
22 | }
23 |
24 | @Nullable
25 | @Override
26 | public TesterProjectSettings getState() {
27 | return settings;
28 | }
29 |
30 | @Override
31 | public void loadState(TesterProjectSettings settings) {
32 | this.settings = settings;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/editor/NamespaceMappingTable.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.projectSettings.editor;
2 |
3 | import com.intellij.execution.util.ListTableWithButtons;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.openapi.util.text.StringUtil;
6 | import com.intellij.util.ui.ColumnInfo;
7 | import com.intellij.util.ui.ListTableModel;
8 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
9 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterNamespaceMapping;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import javax.swing.table.TableCellEditor;
13 |
14 | public class NamespaceMappingTable extends ListTableWithButtons {
15 | @NotNull private final Project project;
16 |
17 | NamespaceMappingTable(@NotNull final Project project) {
18 | super();
19 | this.project = project;
20 | this.getTableView().getEmptyText().setText(TesterBundle.message("settings.namespaceMappings.noMappings"));
21 | }
22 |
23 | @Override
24 | protected ListTableModel createListModel() {
25 | ColumnInfo sourceNamespace = new ElementsColumnInfoBase(TesterBundle.message("settings.namespaceMappings.sourceNamespace")) {
26 | @NotNull
27 | @Override
28 | public String valueOf(TesterNamespaceMapping testerTestMapping) {
29 | return testerTestMapping.getSourceNamespace();
30 | }
31 |
32 | @Override
33 | public boolean isCellEditable(TesterNamespaceMapping testerTestMapping) {
34 | return NamespaceMappingTable.this.canDeleteElement(testerTestMapping);
35 | }
36 |
37 | @Override
38 | public void setValue(TesterNamespaceMapping testerTestMapping, String value) {
39 | testerTestMapping.setSourceNamespace(value);
40 | }
41 |
42 | @NotNull
43 | @Override
44 | protected String getDescription(TesterNamespaceMapping testerTestMapping) {
45 | return valueOf(testerTestMapping);
46 | }
47 |
48 | @NotNull
49 | @Override
50 | public TableCellEditor getEditor(TesterNamespaceMapping testerTestMapping) {
51 | return new NamespaceTableCellEditor(project);
52 | }
53 | };
54 |
55 | ColumnInfo testNamespace = new ElementsColumnInfoBase(TesterBundle.message("settings.namespaceMappings.testsNamespace")) {
56 | @NotNull
57 | @Override
58 | public String valueOf(TesterNamespaceMapping testerTestMapping) {
59 | return testerTestMapping.getTestsNamespace();
60 | }
61 |
62 | @Override
63 | public boolean isCellEditable(TesterNamespaceMapping testerTestMapping) {
64 | return NamespaceMappingTable.this.canDeleteElement(testerTestMapping);
65 | }
66 |
67 | @Override
68 | public void setValue(TesterNamespaceMapping testerTestMapping, String value) {
69 | testerTestMapping.setTestsNamespace(value);
70 | }
71 |
72 | @NotNull
73 | @Override
74 | protected String getDescription(TesterNamespaceMapping testerTestMapping) {
75 | return valueOf(testerTestMapping);
76 | }
77 |
78 | @NotNull
79 | @Override
80 | public TableCellEditor getEditor(TesterNamespaceMapping testerTestMapping) {
81 | return new NamespaceTableCellEditor(project);
82 | }
83 | };
84 |
85 | return new ListTableModel(sourceNamespace, testNamespace);
86 | }
87 |
88 | @Override
89 | protected TesterNamespaceMapping createElement() {
90 | return new TesterNamespaceMapping();
91 | }
92 |
93 | @Override
94 | protected boolean isEmpty(TesterNamespaceMapping testerTestMapping) {
95 | return StringUtil.isEmpty(testerTestMapping.getSourceNamespace())
96 | && StringUtil.isEmpty(testerTestMapping.getTestsNamespace());
97 | }
98 |
99 | @Override
100 | protected TesterNamespaceMapping cloneElement(TesterNamespaceMapping testerTestMapping) {
101 | return testerTestMapping.clone();
102 | }
103 |
104 | @Override
105 | protected boolean canDeleteElement(TesterNamespaceMapping testerTestMapping) {
106 | return true;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/editor/NamespaceTableCellEditor.form:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/editor/NamespaceTableCellEditor.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.projectSettings.editor;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.ui.EditorTextField;
5 | import com.intellij.util.ui.AbstractTableCellEditor;
6 | import com.jetbrains.php.completion.PhpCompletionUtil;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import javax.swing.*;
10 | import java.awt.*;
11 |
12 | public class NamespaceTableCellEditor extends AbstractTableCellEditor {
13 | @NotNull private final Project project;
14 |
15 | private JPanel panel;
16 | private EditorTextField namespaceField;
17 |
18 | NamespaceTableCellEditor(@NotNull final Project project) {
19 | this.project = project;
20 | }
21 |
22 | @Override
23 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
24 | namespaceField.setText(value.toString());
25 | return panel;
26 | }
27 |
28 | @Override
29 | public Object getCellEditorValue() {
30 | return namespaceField.getText();
31 | }
32 |
33 | private void createUIComponents() {
34 | namespaceField = new EditorTextField("", project, null);
35 | PhpCompletionUtil.installNamespaceCompletion(namespaceField, null, () -> {});
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/editor/TesterConfigurableForm.form:
--------------------------------------------------------------------------------
1 |
2 |
72 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/tester/projectSettings/editor/TesterConfigurableForm.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.tester.projectSettings.editor;
2 |
3 | import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.openapi.ui.ComboBox;
6 | import com.intellij.openapi.ui.TextFieldWithBrowseButton;
7 | import com.intellij.openapi.util.text.StringUtil;
8 | import com.intellij.ui.JBColor;
9 | import com.intellij.ui.ToolbarDecorator;
10 | import com.intellij.ui.components.JBLabel;
11 | import com.intellij.util.ui.ElementProducer;
12 | import com.jetbrains.php.util.ConfigurableForm;
13 | import cz.jiripudil.intellij.nette.tester.TesterBundle;
14 | import cz.jiripudil.intellij.nette.tester.TesterUtil;
15 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterNamespaceMapping;
16 | import cz.jiripudil.intellij.nette.tester.projectSettings.TesterProjectSettings;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | import javax.swing.*;
20 | import java.util.List;
21 | import java.util.stream.Collectors;
22 |
23 | public class TesterConfigurableForm implements ConfigurableForm {
24 | private final Project project;
25 | private JPanel panel;
26 |
27 | private JBLabel defaultExtensionLabel;
28 | private ComboBox defaultExtensionCombobox;
29 | private JBLabel bootstrapFileLabel;
30 | private TextFieldWithBrowseButton bootstrapFileField;
31 |
32 | private JPanel namespaceMappingPanel;
33 | private ComboBox testerVersionCombobox;
34 | private JBLabel testerVersionLabel;
35 | private NamespaceMappingTable namespaceMappingTable;
36 |
37 | public TesterConfigurableForm(@NotNull final Project project) {
38 | this.project = project;
39 | }
40 |
41 | @NotNull
42 | @Override
43 | public JComponent getComponent() {
44 | return panel;
45 | }
46 |
47 | @Override
48 | public boolean isModified() {
49 | TesterProjectSettings settings = TesterUtil.getTesterSettings(project);
50 | return settings != null && !(
51 | settings.getDefaultExtension().equals(defaultExtensionCombobox.getSelectedItem())
52 | && settings.getTesterVersion().equals(testerVersionCombobox.getSelectedItem())
53 | && StringUtil.notNullize(settings.getBootstrapFile()).equals(bootstrapFileField.getText())
54 | && settings.getNamespaceMappings().equals(namespaceMappingTable.getTableView().getItems())
55 | );
56 |
57 | }
58 |
59 | @Override
60 | public void apply() {
61 | TesterProjectSettings settings = TesterUtil.getTesterSettings(project);
62 | if (settings == null) {
63 | return;
64 | }
65 |
66 | settings.setDefaultExtension(StringUtil.notNullize((String) defaultExtensionCombobox.getSelectedItem()));
67 | settings.setTesterVersion(StringUtil.notNullize((String) testerVersionCombobox.getSelectedItem()));
68 | settings.setBootstrapFile(bootstrapFileField.getText());
69 |
70 | // lists work with references which complicates detecting modification, cloning each item helps
71 | settings.setNamespaceMappings(cloneNamespaceMappings(namespaceMappingTable.getTableView().getItems()));
72 | }
73 |
74 | @Override
75 | public void reset() {
76 | TesterProjectSettings settings = TesterUtil.getTesterSettings(project);
77 | if (settings == null) {
78 | return;
79 | }
80 |
81 | defaultExtensionCombobox.setSelectedItem(settings.getDefaultExtension());
82 | testerVersionCombobox.setSelectedItem(settings.getTesterVersion().equals("> 2.0") ? ">= 2.0" : settings.getTesterVersion());
83 | bootstrapFileField.setText(settings.getBootstrapFile());
84 |
85 | // lists work with references which complicates detecting modification, cloning each item helps
86 | namespaceMappingTable.getTableView().getTableViewModel().setItems(cloneNamespaceMappings(settings.getNamespaceMappings()));
87 | }
88 |
89 | private List cloneNamespaceMappings(List input) {
90 | return input.stream()
91 | .map(TesterNamespaceMapping::clone)
92 | .collect(Collectors.toList());
93 | }
94 |
95 | private void createUIComponents() {
96 | defaultExtensionLabel = new JBLabel(TesterBundle.message("settings.defaultExtension"));
97 | defaultExtensionCombobox = new ComboBox<>(new String[]{"phpt", "php"});
98 |
99 | testerVersionLabel = new JBLabel(TesterBundle.message("settings.testerVersion"));
100 | testerVersionCombobox = new ComboBox<>(new String[]{"< 2.0", ">= 2.0"});
101 |
102 | bootstrapFileLabel = new JBLabel(TesterBundle.message("settings.bootstrapFile"));
103 | bootstrapFileField = new TextFieldWithBrowseButton();
104 | bootstrapFileField.addBrowseFolderListener(null, null, project, FileChooserDescriptorFactory.createSingleFileDescriptor("php"));
105 |
106 | namespaceMappingTable = new NamespaceMappingTable(project);
107 | namespaceMappingPanel = ToolbarDecorator.createDecorator(namespaceMappingTable.getTableView(), new ElementProducer() {
108 | @Override
109 | public TesterNamespaceMapping createElement() {
110 | return new TesterNamespaceMapping();
111 | }
112 |
113 | @Override
114 | public boolean canCreateElement() {
115 | return true;
116 | }
117 | }).createPanel();
118 |
119 | namespaceMappingPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, JBColor.LIGHT_GRAY), TesterBundle.message("settings.namespaceMappings.title")));
120 | }
121 | }
122 |
--------------------------------------------------------------------------------