Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert the testmethod modifier to the @IsTest annotation #19

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

## Formatting Changes

### Convert `testmethod` modifier to the `@IsTest` annotation

The `apexFormatAnnotations` option now will convert all `testmethod` method modifiers to the equivalent annotation. If the option `apexFormatAnnotations` is set to `true` then...

This:

```java
public testmethod static someTestMethod() {
}
```

Will become this:

```java
@IsTest
public static someTestMethod() {
}
```

### Convert `webservice` and `testmethod` modifier to lowecase

Was:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Fix an issue with the `apexSortModifiers` option which removes a method modifier if there is an own-line comment between annotation and method ([issue](https://github.com/IlyaMatsuev/prettier-plugin-apex/issues/13)).
- Fix an issue with the `apexExplicitAccessModifier` option when the `webservice` method gets an access modifier ([issue](https://github.com/IlyaMatsuev/prettier-plugin-apex/issues/12)).
- Fix an issue with the `apexExplicitAccessModifier` option when the method parameters get `private` modifier ([issue](https://github.com/IlyaMatsuev/prettier-plugin-apex/issues/11)).
- Update the `apexFormatAnnotations` option to convert the `testmethod` method modifier to the `@IsTest` annotation ([issue](https://github.com/IlyaMatsuev/prettier-plugin-apex/issues/10)).
- Change the formatting of `webservice` and `testmethod` modifiers to be always printed in lowercase instead of `webService` and `testMethod`.

# 2.0.3
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ This is Not Opinionated formatter, so the there are multiple configurations avai
| `apexStandalonePort` | `2117` | The port that the standalone Apex parser listens on.<br>Only applicable if `apexStandaloneParser` is `built-in`. |
| `apexStandaloneHost` | `localhost` | The host that the standalone Apex parser listens on.<br>Only applicable if `apexStandaloneParser` is `built-in`. |
| `apexInsertFinalNewline` | `true` | Whether a newline is added as the last thing in the output |
| `apexFormatAnnotations` | `false` | Format Apex annotations to the upper camel case. |
| `apexFormatAnnotations` | `false` | Format Apex annotations to the upper camel case. Also converts `testmethod` modifier to `@IsTest` case. |
| `apexFormatStandardTypes` | `false` | Format the most popular Apex standard types. For example: `System`, `Map`, `DateTime`, `SObject` etc. |
| `apexFormatInlineComments` | `false` | Format the inline comments to have a space and start from a capital letter. |
| `apexAnnotationsArgsSpacing` | `false` | Add spaces between an annotation argument and value. For example: `@Future(callout = true)`. |
Expand Down
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,10 @@ export const ACCESS_MODIFIERS = ["private", "protected", "public", "global"];

export const ACCESS_EXCEPTION_MODIFIERS = ["webservice"];

export const ABSTRACT_MODIFIER = "abstract";

export const TESTMETHOD_MODIFIER = "testmethod";

export const MODIFIERS_PRIORITY = [
...ACCESS_MODIFIERS,
...ACCESS_EXCEPTION_MODIFIERS,
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ export const options = {
type: "boolean",
category: CATEGORY_APEX,
default: false,
description: "Format Apex annotations to the upper camel case.",
description:
"Format Apex annotations to the upper camel case. Also converts `testmethod` modifier to `@IsTest`",
},
apexFormatStandardTypes: {
type: "boolean",
Expand Down
73 changes: 46 additions & 27 deletions src/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import {
ACCESS_EXCEPTION_MODIFIERS,
DEFAULT_ACCESS_MODIFIER,
MODIFIERS_PRIORITY,
TESTMETHOD_MODIFIER,
ABSTRACT_MODIFIER,
} from "./constants";
import jorje from "../vendor/apex-ast-serializer/typings/jorje";
import Concat = builders.Concat;
Expand Down Expand Up @@ -82,37 +84,60 @@ function pushIfExist(
return parts;
}

function hasModifiers(modifiers: Doc[], search: Doc[]): boolean {
function hasModifiers(modifiers: Doc[], ...search: Doc[]): boolean {
return !!findInDoc(
modifiers,
(d) => (search.some((a) => d === a) ? d : undefined),
(d) =>
search.some((a) => {
if (typeof d === "string" && typeof a === "string") {
return d.toLowerCase() === a.toLowerCase();
}
return d === a;
})
? d
: undefined,
undefined,
);
}

function handleModifiersOptions(
modifiers: Doc[],
options: ParserOptions,
): Doc[] {
function normalizeModifiers(modifiers: Doc[], options: ParserOptions): Doc[] {
// For annotation there is always "@" at the beginning of the Doc
const annotationModifiers = modifiers.filter(
(m) => Array.isArray(m) && m[0] === "@",
);
let nonAnnotationModifiers = modifiers.filter(
(m) => !Array.isArray(m) || m[0] !== "@",
);
let nonAnnotationModifiers = modifiers
.filter((m) => !Array.isArray(m) || m[0] !== "@")
.flat(2);

// Replace "testmethod" modifier with the "@IsTest" annotation
if (
options.apexFormatAnnotations &&
hasModifiers(nonAnnotationModifiers, TESTMETHOD_MODIFIER)
) {
// Delete "testmethod" modifier and space after it
nonAnnotationModifiers.splice(
nonAnnotationModifiers.indexOf(TESTMETHOD_MODIFIER),
2,
);
if (!hasModifiers(annotationModifiers, "istest")) {
annotationModifiers.unshift(["@IsTest", hardline]);
}
}

// Add default modifier (private) if the option is enabled
if (
options.apexExplicitAccessModifier &&
options.parser === "apex" &&
!hasModifiers(modifiers, [
!hasModifiers(
nonAnnotationModifiers,
...ACCESS_MODIFIERS,
...ACCESS_EXCEPTION_MODIFIERS,
])
)
) {
nonAnnotationModifiers.unshift([DEFAULT_ACCESS_MODIFIER, " "]);
nonAnnotationModifiers.unshift(DEFAULT_ACCESS_MODIFIER, " ");
}

// Sort modifiers if the option is enabled
if (options.apexSortModifiers && nonAnnotationModifiers.length) {
const danglingComments = modifiers
.flat(2)
Expand All @@ -127,7 +152,6 @@ function handleModifiersOptions(
nonAnnotationModifiers = [
...danglingComments,
nonAnnotationModifiers
.flat(2)
.filter((m) => typeof m === "string" && m.trim())
.sort((a, b) => {
const aPriority = MODIFIERS_PRIORITY.indexOf(a as string);
Expand All @@ -144,6 +168,7 @@ function handleModifiersOptions(
" ",
];
}

// Put annotations first
return [...annotationModifiers, ...nonAnnotationModifiers];
}
Expand Down Expand Up @@ -667,7 +692,7 @@ function handleInterfaceDeclaration(
const node = path.getValue();

const superInterface: Doc = path.call(print, "superInterface", "value");
const modifierDocs: Doc[] = handleModifiersOptions(
const modifierDocs: Doc[] = normalizeModifiers(
path.map(print, "modifiers"),
options,
);
Expand Down Expand Up @@ -733,7 +758,7 @@ function handleClassDeclaration(
): Doc {
const node = path.getValue();
const superClass: Doc = path.call(print, "superClass", "value");
const modifierDocs: Doc[] = handleModifiersOptions(
const modifierDocs: Doc[] = normalizeModifiers(
path.map(print, "modifiers"),
options,
);
Expand Down Expand Up @@ -950,7 +975,7 @@ function handlePropertyDeclaration(
print: printFn,
options: ParserOptions,
): Doc {
const modifierDocs: Doc[] = handleModifiersOptions(
const modifierDocs: Doc[] = normalizeModifiers(
path.map(print, "modifiers"),
options,
);
Expand Down Expand Up @@ -1017,13 +1042,10 @@ function handleMethodDeclaration(
const parts: Doc[] = [];
const parameterParts = [];
// Modifiers
if (statementDoc || hasModifiers(modifierDocs, ["abstract"])) {
// There is no statement doc if this is an interface method or abstract method
if (statementDoc || hasModifiers(modifierDocs, ABSTRACT_MODIFIER)) {
// There is no statement if this is an interface method or abstract method
// But for abstract methods it's possible to have access modifier
modifierDocs = handleModifiersOptions(
path.map(print, "modifiers"),
options,
);
modifierDocs = normalizeModifiers(modifierDocs, options);
}
if (modifierDocs.length > 0) {
parts.push(modifierDocs);
Expand Down Expand Up @@ -1127,7 +1149,7 @@ function handleEnumDeclaration(
print: printFn,
options: ParserOptions,
): Doc {
const modifierDocs: Doc[] = handleModifiersOptions(
const modifierDocs: Doc[] = normalizeModifiers(
path.map(print, "modifiers"),
options,
);
Expand Down Expand Up @@ -1362,10 +1384,7 @@ function handleVariableDeclarations(
// Modifiers
if (path.getParentNode()["@class"] === APEX_TYPES.FIELD_MEMBER) {
// Add private access modifier if this is a class field
modifierDocs = handleModifiersOptions(
path.map(print, "modifiers"),
options,
);
modifierDocs = normalizeModifiers(path.map(print, "modifiers"), options);
}
parts.push(join("", modifierDocs));

Expand Down
24 changes: 24 additions & 0 deletions tests/annotated_method/AnnotatedMethod.cls
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,28 @@ class topLevelClass {

@InvocableVariable(label='Records for Input' description='yourDescription' required=true)
public List<SObject> inputCollection;

static testmethod void someTest() {

}

@SuppressWarnings('PMD')
static testmethod void someTest() {

}

@istest
public static testmethod void someTest() {

}

@istest(SeeAllData=true)
static testmethod void someTest() {

}

@istest(SeeAllData=true)
static void someTest() {

}
}
40 changes: 40 additions & 0 deletions tests/annotated_method/__snapshots__/jsfmt.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ class topLevelClass {

@InvocableVariable(label='Records for Input' description='yourDescription' required=true)
public List<SObject> inputCollection;

static testmethod void someTest() {

}

@SuppressWarnings('PMD')
static testmethod void someTest() {

}

@istest
public static testmethod void someTest() {

}

@istest(SeeAllData=true)
static testmethod void someTest() {

}

@istest(SeeAllData=true)
static void someTest() {

}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class topLevelClass {
Expand Down Expand Up @@ -61,6 +85,22 @@ class topLevelClass {
required = true
)
public List<SObject> inputCollection;

@IsTest
static void someTest() {}

@IsTest
@SuppressWarnings('PMD')
static void someTest() {}

@IsTest
public static void someTest() {}

@IsTest(SeeAllData = true)
static void someTest() {}

@IsTest(SeeAllData = true)
static void someTest() {}
}

`;
17 changes: 16 additions & 1 deletion tests/comments/Comments.cls
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,16 @@ class Comments {
System.debug('Hi');
}

@isTest // Decorator Trailing Inline Comment 1
static void trailingCommentAfterMethodName() {
System.debug('Hi');
}

@isTest // Decorator Trailing Inline Comment 1
testmethod static void trailingCommentAfterMethodName() {
System.debug('Hi');
}

/* Decorator Leading Block Comment 1 */ @isTest
void leadingCommentBeforeAnnotation() {
System.debug('Hi');
Expand All @@ -856,6 +866,11 @@ class Comments {
System.debug('Hi');
}

@isTest /* Decorator Trailing Block Comment 1 */
static testmethod void trailingCommentAfterMethodName() {
System.debug('Hi');
}

/* Decorator Leading Block Comment 1 */ @isTest
static void trailingCommentAfterMethodName() {
System.debug('Hi');
Expand All @@ -865,7 +880,7 @@ class Comments {
seeAllData = true // Method annotation inline comment 1
//isParallel = false // Method annotation inline comment 2
)
void methodAnnotationInlineComment() {
testmethod void methodAnnotationInlineComment() {
}

@isTest(
Expand Down
Loading