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

Feature/1135 formatter naming conventions #1250

Merged
merged 4 commits into from
Dec 24, 2024
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
7 changes: 7 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ https://eslint.org/
We use **prettier** to format scss json and yaml files:
https://prettier.io/

We use **prettier** to format scss json and yaml files:
https://prettier.io/

If you get a does not match pattern error, it is likely because of the custom words for the OKR tool
here is a regex101 project that showcases how the regex works:
https://regex101.com/r/VUyAt6/1

## Test Coverage

For generating the coverage of our frontend specs (jest), you can use following command:
Expand Down
22 changes: 11 additions & 11 deletions frontend/cypress/e2e/check-in.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as users from '../fixtures/users.json';
import { uniqueSuffix } from '../support/helper/utils';
import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage';
import { Unit } from '../../src/app/shared/types/enums/Unit';
import { Unit } from '../../src/app/shared/types/enums/unit';
import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage';
import CheckInDialog from '../support/helper/dom-helper/dialogs/checkInDialog';
import CheckInHistoryDialog from '../support/helper/dom-helper/dialogs/checkInHistoryDialog';
Expand Down Expand Up @@ -126,7 +126,7 @@ describe('okr check-in', () => {
.fillCheckInInitiatives('Will be difficult')
.submit();
keyResultDetailPage
.showAllCheckins()
.showAllCheckIns()
.checkForAttribute('Confidence:', '5 / 10')
.checkForAttribute('Confidence:', '6 / 10')
.checkForAttribute('Veränderungen:', 'We bought a new house')
Expand Down Expand Up @@ -156,7 +156,7 @@ describe('okr check-in', () => {
.fillCheckInInitiatives('A cat would be great')
.submit();
cy.contains('Aktuell: 30 CHF');
keyResultDetailPage.showAllCheckins();
keyResultDetailPage.showAllCheckIns();
cy.contains('Check-in History');
cy.contains('Wert: 30 CHF');
CheckInHistoryDialog.do()
Expand Down Expand Up @@ -191,7 +191,7 @@ describe('okr check-in', () => {
.fillCheckInInitiatives('A cat would be great')
.submit();
cy.contains('Aktuell: 30 EUR');
keyResultDetailPage.showAllCheckins();
keyResultDetailPage.showAllCheckIns();
cy.contains('Check-in History');
cy.contains('Wert: 30 EUR');
CheckInHistoryDialog.do()
Expand All @@ -213,7 +213,7 @@ describe('okr check-in', () => {
.fillCheckInInitiatives('A cat would be great')
.submit();
cy.contains('Aktuell: 30 FTE');
keyResultDetailPage.showAllCheckins();
keyResultDetailPage.showAllCheckIns();
cy.contains('Check-in History');
cy.contains('Wert: 30 FTE');
});
Expand All @@ -233,7 +233,7 @@ describe('okr check-in', () => {
.fillCheckInCommentary('There is a new car')
.fillCheckInInitiatives('Buy now a new pool')
.submit();
keyResultDetailPage.showAllCheckins()
keyResultDetailPage.showAllCheckIns()
.editLatestCheckIn();
cy.contains('For editing ordinal checkin');
cy.contains('Confidence um Target Zone zu erreichen');
Expand Down Expand Up @@ -308,14 +308,14 @@ function getCurrentDate() {
const mm = today.getMonth() + 1; // Months start at 0!
const dd = today.getDate();

let dd_str = '' + dd;
let mm_str = '' + mm;
let ddStr = '' + dd;
let mmStr = '' + mm;
if (dd < 10) {
dd_str = '0' + dd_str;
ddStr = '0' + ddStr;
}
if (mm < 10) {
mm_str = '0' + mm_str;
mmStr = '0' + mmStr;
}

return dd_str + '.' + mm_str + '.' + yyyy;
return ddStr + '.' + mmStr + '.' + yyyy;
}
2 changes: 1 addition & 1 deletion frontend/cypress/e2e/key-result.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as users from '../fixtures/users.json';
import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage';
import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage';
import { Unit } from '../../src/app/shared/types/enums/Unit';
import { Unit } from '../../src/app/shared/types/enums/unit';
import KeyResultDialog from '../support/helper/dom-helper/dialogs/keyResultDialog';

describe('okr key-result', () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/cypress/e2e/scoring.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as users from '../fixtures/users.json';
import { getPercentageMetric, getPercentageOrdinal } from 'cypress/support/helper/scoringSupport';
import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage';
import { Unit } from '../../src/app/shared/types/enums/Unit';
import { Unit } from '../../src/app/shared/types/enums/unit';
import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage';

describe('okr scoring', () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/cypress/e2e/tab.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as users from '../fixtures/users.json';
import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage';
import { Unit } from '../../src/app/shared/types/enums/Unit';
import { Unit } from '../../src/app/shared/types/enums/unit';
import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage';

describe('tabbing workflows', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Dialog from './dialog';
import { Unit } from '../../../../../src/app/shared/types/enums/Unit';
import { Unit } from '../../../../../src/app/shared/types/enums/unit';
import ConfirmDialog from './confirmDialog';
import Chainable = Cypress.Chainable;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default class KeyResultDetailPage extends Page {
return new KeyResultDialog();
}

showAllCheckins() {
showAllCheckIns() {
this.elements.showAllCheckins()
.click();
return new CheckInHistoryDialog();
Expand Down
63 changes: 61 additions & 2 deletions frontend/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,42 @@ import stylistic from '@stylistic/eslint-plugin'
import html from '@html-eslint/eslint-plugin'
import angular from 'angular-eslint'
import htmlParser from '@html-eslint/parser'
import { createRegexForWords } from './eslintHelper.mjs'
import checkFile from 'eslint-plugin-check-file'

export default tsEslint.config(
{
files: ['src/app/shared/types/**/*'],
rules: {
'check-file/filename-naming-convention': [
'error',
{
'**/*.{js,ts}': 'KEBAB_CASE',
},
],
},
},
{
ignores: ['cypress/downloads/**/*'],
},
{
files: ['**/*.ts'],
files: ['src/**/*.ts', 'cypress/**/*.ts'],
extends: [
eslint.configs.recommended,
...tsEslint.configs.recommended,
...tsEslint.configs.stylistic,
...angular.configs.tsRecommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.json', './tsconfig.spec.json'],
},
},
processor: angular.processInlineTemplates,
languageOptions: {
parserOptions: {
project: ['./tsconfig.json', './tsconfig.spec.json'],
},
globals: {
//Cypress things not recognized by eslint
cy: 'readonly',
Expand Down Expand Up @@ -130,10 +151,40 @@ export default tsEslint.config(
style: 'kebab-case',
},
],
'@typescript-eslint/naming-convention': [
'error',
{
selector: ['class', 'interface'],
format: ['PascalCase'],
},
{
selector: 'variable',
modifiers: [],
format: ['camelCase', 'UPPER_CASE'],
},
{
selector: 'enum',
format: ['PascalCase'],
},
{
selector: 'enumMember',
format: ['UPPER_CASE'],
},
{
selector: ['method', 'function'],
format: ['camelCase'],
},
{
selector: 'typeParameter',
format: ['PascalCase'],
},
],
'id-match': ['error', createRegexForWords(['KeyResult', 'CheckIn', 'TeamManagement', 'StretchGoal'])],
},
},

{
files: ['**/*.spec.ts'],
files: ['src/**/*.spec.ts', 'cypress/**/*.spec.ts'],
extends: [...tsEslint.configs.recommended],
rules: {
//Rules removed for Test files because they are unnecessary for tests
Expand All @@ -159,6 +210,13 @@ export default tsEslint.config(
'@html-eslint/require-img-alt': 'off',
'@html-eslint/indent': ['error', 2],
'@html-eslint/require-closing-tags': ['error', { selfClosing: 'always' }],
'@html-eslint/id-naming-convention': [
'error',
'regex',
{
pattern: `(?=(^[a-z]+(-[a-z]+)*$))(?=(${createRegexForWords(['KeyResult', 'CheckIn', 'TeamManagement', 'StretchGoal'])}))`,
},
],
//Doesn't work with Angular 17+
'@html-eslint/element-newline': 'off',
},
Expand All @@ -168,6 +226,7 @@ export default tsEslint.config(
'unused-imports': unusedImports,
'@stylistic': stylistic,
'@html-eslint': html,
'check-file': checkFile,
},
}
)
59 changes: 59 additions & 0 deletions frontend/eslintHelper.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export function createRegexForWords(wordList) {
// This function builds a case-insensitive regex pattern for a given word.

const part1List = []
const part2List = []

wordList.forEach((word) => {
const part1 = createRegexToCheckIfWordLookAlike(word)
const part2 = createFallbackRegex(word)
part1List.push(part1)
part2List.push(part2)
})
return `(${part1List.join('|')})|(${part2List.join('')})`
}

function getCaseInsensitiveRegexForChar(c) {
return `[${c.toUpperCase()}${c.toLowerCase()}]`
}

function createCaseInsensitiveRegexForWord(word) {
return word
.match(/[A-Z][a-z]+/g)
.join('.?')
.split('')
.map((c) => (/[a-zA-Z]/g.test(c) ? getCaseInsensitiveRegexForChar(c) : c))
.join('')
}

function transformToUnderscoreUppercase(word) {
return word
.split(/(?=[A-Z])/) // Split at uppercase letters without removing them
.join('_')
.toUpperCase()
}

function transformToHyphenLowercase(word) {
return word
.split(/(?=[A-Z])/) // Split at uppercase letters without removing them
.join('-')
.toLowerCase()
}

function getWordRegexWithOptionalLetters(word) {
return word.replace(/(\[[^\[\]]+\])(?![.?])/g, '$1?') // Puts a "?" between the case-insensitive braces if there is no "?" or "." already
}

function createRegexToCheckIfWordLookAlike(word) {
let wordLooksLikeRegex = createCaseInsensitiveRegexForWord(word)
wordLooksLikeRegex = getWordRegexWithOptionalLetters(wordLooksLikeRegex)
const wordCorrectRegex = getCaseInsensitiveRegexForChar(word[0]) + word.slice(1)
const wordInUpperCase = transformToUnderscoreUppercase(word)
const wordInLowerCase = transformToHyphenLowercase(word)
return `(?=.*${wordLooksLikeRegex}.*)(.*${wordCorrectRegex}.*|[A-Z_]*${wordInUpperCase}[A-Z_]*|[a-z-]*${wordInLowerCase}[a-z-]*)`
}

function createFallbackRegex(word) {
const caseInsensitiveWordRegex = createCaseInsensitiveRegexForWord(word)
return `(?=^(?!.*${caseInsensitiveWordRegex}).*)`
}
31 changes: 31 additions & 0 deletions frontend/eslintHelper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as eslintHelper from "./eslintHelper.mjs"
import { $locationShim } from "@angular/common/upgrade";

describe("eslintHelper", () => {
const combinedRegex:string = "((?=.*[Kk]?[Ee]?[Yy].?[Rr]?[Ee]?[Ss]?[Uu]?[Ll]?[Tt]?.*)(.*[Kk]eyResult.*|[A-Z_]*KEY_RESULT[A-Z_]*|[a-z-]*key-result[a-z-]*)|(?=.*[Cc]?[Hh]?[Ee]?[Cc]?[Kk].?[Ii]?[Nn]?.*)(.*[Cc]heckIn.*|[A-Z_]*CHECK_IN[A-Z_]*|[a-z-]*check-in[a-z-]*))|((?=^(?!.*[Kk][Ee][Yy].?[Rr][Ee][Ss][Uu][Ll][Tt]).*)(?=^(?!.*[Cc][Hh][Ee][Cc][Kk].?[Ii][Nn]).*))"
const keyResultRegex:string = "((?=.*[Kk]?[Ee]?[Yy].?[Rr]?[Ee]?[Ss]?[Uu]?[Ll]?[Tt]?.*)(.*[Kk]eyResult.*|[A-Z_]*KEY_RESULT[A-Z_]*|[a-z-]*key-result[a-z-]*))|((?=^(?!.*[Kk][Ee][Yy].?[Rr][Ee][Ss][Uu][Ll][Tt]).*))"
const checkInRegex:string = "((?=.*[Cc]?[Hh]?[Ee]?[Cc]?[Kk].?[Ii]?[Nn]?.*)(.*[Cc]heckIn.*|[A-Z_]*CHECK_IN[A-Z_]*|[a-z-]*check-in[a-z-]*))|((?=^(?!.*[Cc][Hh][Ee][Cc][Kk].?[Ii][Nn]).*))"

it.each([
[["KeyResult"], keyResultRegex],
[["CheckIn"], checkInRegex],
[["KeyResult", "CheckIn"], combinedRegex],
])("should return regex %p", (wordToRegex, expectedRegex) => {
expect(eslintHelper.createRegexForWords(wordToRegex)).toEqual(expectedRegex);
});

it.each([
[["KeyResult"], ["KeyResult", "CurrentKeyResult", "keyResult", "keyResultId", "key-result", "test-key-result-test"],["Keyresult", "CurrentKeyresult", "keyresult", "keyresultId", "KEyResult", "KeyResUlt", "test-keyresult-test"]],
[["CheckIn"], ["CheckIn", "CurrentCheckIn", "checkIn", "checkInId", "check-in", "test-check-in-test"],["Checkin", "CurrentCheckin", "checkin", "checkinId", "cHeckIn", "checkIN", "test-checkin-test"]],
[["KeyResult", "CheckIn"], ["KeyResult", "CurrentKeyResult", "keyResult", "keyResultId", "key-result", "test-key-result-test", "CheckIn", "CurrentCheckIn", "checkIn", "checkInId", "check-in", "test-check-in-test"],["Keyresult", "CurrentKeyresult", "keyresult", "keyresultId", "KEyResult", "KeyResUlt", "test-keyresult-test", "Checkin", "CurrentCheckin", "checkin", "checkinId", "cHeckIn", "checkIN", "test-checkin-test"]],
])("should run regex %p threw the matching and not matching list", (wordToRegex:string[], matchingListToRegex:string[], notMatchingListToRegex:string[]) => {
const regexOfCustomWord = new RegExp(eslintHelper.createRegexForWords(wordToRegex));
matchingListToRegex = matchingListToRegex.filter(word => regexOfCustomWord.test(word));
notMatchingListToRegex = notMatchingListToRegex.filter(word => regexOfCustomWord.test(word));

expect(matchingListToRegex.length).toBe(matchingListToRegex.length);
expect(notMatchingListToRegex.length).toBe(0)
});

})

28 changes: 25 additions & 3 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,17 @@
"@eslint/js": "^9.15.0",
"@html-eslint/eslint-plugin": "^0.27.0",
"@html-eslint/parser": "^0.27.0",
"@stylistic/eslint-plugin": "^2.11.0",
"@types/eslint__js": "^8.42.3",
"@types/jest": "^29.5.13",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"angular-eslint": "19.0.2",
"browserslist": "^4.24.2",
"cypress": "^13.15.0",
"cypress-real-events": "^1.13.0",
"eslint": "^9.16.0",
"eslint-plugin-check-file": "^2.8.0",
"eslint-plugin-unused-imports": "^4.1.4",
"jest": "^29.7.0",
"jest-preset-angular": "^14.2.4",
Expand Down
Loading
Loading