Code Conventions
Accessing Elements
Template Refs
Use Vue template refs to access elements within component code.
Integration and Unit Tests
Use data-testid attributes for integration or unit tests.
- the attribute must be written as
data-testid(no double dashes, no underscores) - do not use uppercase characters
- use kebab-case for the value (e.g.
data-testid="submit-button")
ts-ignore-comments
Try to avoid // @ts-ignore and try to define the types of variables everytime.
Composables
Composables are a great way to make the code shareable among components or other composables. If you want to write a composable, consider using one of these well documented and well tested ones: VueUse - Collection of Vue Composition Utilities
If you write a composable:
- its file name follows the pattern
<name>.composable.ts, e.g.foo-bar.composable.ts - place it in
/src/composablesonly if it is used across multiple building blocks
CSS / Styling
-
Global styles live in a central
styles/directory, imported viamain.ts -
Use scoped styles by default in page and module components:
<style scoped lang="scss">.card-header {font-size: var(--heading-3);}</style> -
Font sizes via CSS custom properties defined in
src/styles/css-variables/_typography.scss— do not use hardcoded pixel values:// Badfont-size: 22px;// Goodfont-size: var(--heading-3); -
z-index values via CSS custom properties in
src/styles/css-variables/_z-index.scss— no magic numbers:// Badz-index: 9999;// Goodz-index: var(--z-overlay); -
Colors come from the Vuetify theme /
src/themes/— no hardcoded color values in component styles:// Badcolor: #9e292b;// Goodcolor: rgba(var(--v-theme-primary)); -
Font changes exclusively in
src/styles/utility/_fonts.scss -
Refer to the official Vue Style Guide for component conventions
Test Conventions
Test Filename Conventions
Test files follow the same name as the file under test, with .unit.ts as an additional extension:
HelloWorld.vue → HelloWorld.unit.ts
foo-bar.composable.ts → foo-bar.composable.unit.ts
Setup Methods
Separate test setup from actual tests by writing a setup function. Keep it
reusable and configurable to avoid redundant code across test groups:
const setup = (props?: Partial<MyComponent['$props']>) => {
const wrapper = mount(MyComponent, {
props: {
label: "default label",
...props,
},
});
return { wrapper };
};
it('should display the label', () => {
const { wrapper } = setup({ label: "custom label" });
// ...
});
it('should not render the button', () => {
const { wrapper } = setup();
// ...
});
Structure tests using "describe"-blocks
Especially in large test-files it is very helpful for the reader to have a tree-like structure grouping the tests. Use describe blocks to group tests that are related to the same aspect of your code/the functionality.
- describe block that contains the filename in the root-level of the test-file
- sub-describe-blocks for groups of tests focussing the same aspects of your code
Example:
describe('ImportModal', () => {
describe('when action button is clicked', () => {
// ...
});
describe("when backend returns an error", () => {
// ...
});
});
Test Naming
Use it instead of test and phrase each test as a natural sentence starting with "should":
// Bad
it('name changes on button click', ...);
// Good
it('should display the info text', ...);
it('should not render migration start button', ...);
it('should return the translation', ...);
data-testids
Refer to Integration and Unit Tests for the naming convention. Test only input and output of your component, not internal implementation details. Use data-testids to check for the presence of certain elements or their content, but do not check for internal variables or methods of the component.
it('should display the label', () => {
const { wrapper } = setup({ label: "custom label" });
expect(wrapper.find('[data-testid="label"]').text()).toBe("custom label");
});