diff --git a/README.md b/README.md index c4c6a15..0be7a5a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Code Style Guide +> Our project is based on the [airbnb](https://github.com/airbnb/javascript) and [TypeScript style guide](https://ts.dev/style/#identifiers) project fork to adapt the specification for the Dtstack Frontend Team. +> Compared to the original, it integrates the Dtstack Frontend Team specification, adds the latest JS and React features, and unit testing. +> We plan to sort out a set of corresponding link rules according to the specifications in the future and integrate them into our projects for actual production and development. + ## Code Style guide directory - [React](react/README.md) @@ -20,3 +24,9 @@ Because software is often written by different people throughout its life cycle. - Regular code can reduce maintenance costs - Normative code facilitates code review - Getting into the habit of code specification helps programmers grow + +## Thank you +Thanks for helping us as we sort through the code specification: + +- [TypeScript style guide](https://ts.dev/style/#identifiers) +- [airbnb](https://github.com/airbnb/javascript) \ No newline at end of file diff --git a/css-sass/README.md b/css-sass/README.md index 89b8e8e..6da69f0 100644 --- a/css-sass/README.md +++ b/css-sass/README.md @@ -58,8 +58,8 @@ Finally, properties are what give the selected elements of a rule declaration th ```css /* some selector */ { - background: #f1f1f1; color: #333; + background: #f1f1f1; } ``` @@ -84,7 +84,7 @@ Finally, properties are what give the selected elements of a rule declaration th ```css .avatar{ border-radius:50%; - border:2px solid white; } + border:2px solid #fff;} .no, .nope, .not_good { // ... } @@ -98,7 +98,7 @@ Finally, properties are what give the selected elements of a rule declaration th ```css .avatar { border-radius: 50%; - border: 2px solid white; + border: 2px solid #fff; } .one, @@ -142,13 +142,10 @@ We encourage some combination of OOCSS and BEM for these reasons: function ListingCard() { return ( ); } @@ -156,15 +153,15 @@ We encourage some combination of OOCSS and BEM for these reasons: ```css /* ListingCard.css */ - .ListingCard { } - .ListingCard--featured { } - .ListingCard__title { } - .ListingCard__content { } + .listing-card { } + .listing-card-featured { } + .listing-card_title { } + .listing-card_content { } ``` - - `.ListingCard` is the “block” and represents the higher-level component - - `.ListingCard__title` is an “element” and represents a descendant of `.ListingCard` that helps compose the block as a whole. - - `.ListingCard--featured` is a “modifier” and represents a different state or variation on the `.ListingCard` block. + - `.listing-card` is the “block” and represents the higher-level component + - `.listing-card_title` is an “element” and represents a descendant of `.listing-card` that helps compose the block as a whole. + - `.listing-card-featured` is a “modifier” and represents a different state or variation on the `.listing-card` block. ### ID selectors diff --git a/javascript/README.md b/javascript/README.md index 1f74f59..b89c09b 100644 --- a/javascript/README.md +++ b/javascript/README.md @@ -1121,7 +1121,7 @@ const inherits = require('inherits'); function PeekableQueue(contents) { Queue.apply(this, contents); - } + }; inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0]; @@ -1608,6 +1608,16 @@ // good const binary = 2 ** 10; ``` + + - [12.4](#optional--chaining) The optional chaining (?.) expression can short-circuit with a return value of undefined. Therefore, treating an evaluated optional chaining expression as a function, object, number, etc., can cause TypeError or unexpected results. eslint: [`no-unsafe-optional-chaining`](https://eslint.org/docs/rules/no-unsafe-optional-chaining#disallow-use-of-optional-chaining-in-contexts-where-the-undefined-value-is-not-allowed-no-unsafe-optional-chaining). + > This rule aims to detect some cases where the use of optional chaining doesn't prevent runtime errors. In particular, it flags optional chaining expressions in positions where short-circuiting to undefined causes throwing a TypeError afterward. + ```javascript + // bad + (obj?.foo)(); + + // good + (obj?.foo)?.(); + ``` **[⬆ back to top](#table-of-contents)** diff --git a/typescript/README.md b/typescript/README.md index 8774a04..300050f 100644 --- a/typescript/README.md +++ b/typescript/README.md @@ -249,6 +249,15 @@ There are two types of comments, JSDoc (`/** ... _/`) and non-JSDoc ordinary com - Use `/** JSDoc _/` comments for documentation, i.e. comments a user of the code should read. - Use `// line comments` for implementation comments, i.e. comments that only concern the implementation of the code itself. +```ts +// Bad +const active: boolean = true; // is current tab + +// Good +// is current tab +const active: boolean = true; +``` + JSDoc comments are understood by tools (such as editors and documentation generators), while ordinary comments are only for other humans. #### JSDoc rules follow the JavaScript style @@ -2027,6 +2036,22 @@ TypeScripts `any` type is a super and subtype of all other types, and allows der - Use `unknown` - Suppress the lint warning and document why +#### Any attribute + +Sometimes we want an interface to allow any other attributes in addition to the required and optional attributes. At this time, we can use the form of index signature to meet the above requirements. + +```ts +interface Person { + name: string; + age?: number; + [propName: string]: any; +} + +const p1 = { name: "semlinker" }; +const p2 = { name: "lolo", age: 5 }; +const p3 = { name: "kakuqo", sex: 1 }; +``` + #### Providing a more specific type Use interfaces, an inline object type, or a type alias: @@ -2075,6 +2100,16 @@ danger.whoops(); // This access is completely unchecked! To safely use `unknown` values, narrow the type using a type guard +```ts +let value: unknown; +let value1: unknown = value; // OK +let value2: any = value; // OK +let value3: boolean = value; // Error +let value4: number = value; // Error +``` + +`unknown` type can only be assigned to `any` type and `unknown` type itself. + #### Suppressing `any` lint warnings Sometimes using `any` is legitimate, for example in tests to construct a mock object. In such cases, add a comment that suppresses the lint warning, and document why it is legitimate. diff --git a/unit-test/README.md b/unit-test/README.md index 96b3a21..ed0f83c 100644 --- a/unit-test/README.md +++ b/unit-test/README.md @@ -5,52 +5,69 @@ 在我们的业务开发中,通常应用的是敏捷开发的模型。在此类模型中,单元测试在大部分情况下是为了确保代码的正常运行以及防止在未来迭代的过程中出现问题。那么我们应该如何在业务开发中书写测试用例呢? ## 规范 + ### 1. 工具 -在袋鼠云数栈团队,我们建议使用 `[jest](https://github.com/facebook/jest)` + `[@testing-library/react](https://github.com/testing-library/react-testing-library)` 来书写测试用例。后者是为 `DOM` 和 `UI` 组件测试的软件工具。 + +在袋鼠云数栈团队,我们建议使用 [`jest`](https://github.com/facebook/jest) + [`@testing-library/react`](https://github.com/testing-library/react-testing-library) 来书写测试用例。后者是为 `DOM` 和 `UI` 组件测试的软件工具。 + ### 2. 描述 + 在用 `describe` 或者 `test\it` 函数时候,通常我们需要用描述当前测试用例测试的内容,我们建议**首字母大写**,并且尽量保证语句的通顺。 + ### 3. cleanup + 在每一个测试用例结束之后,确保所有的状态能回归到最初状态,比如在 UI 组件测试中,我们建议在 `afterEach` 中调用 `cleanup` 函数 ```ts -import { cleanup } from '@testing-library/react'; +import { cleanup } from "@testing-library/react"; -describe('For test', () => { - afterEach(cleanup); +describe("For test", () => { + afterEach(cleanup); - test('...', () => {}) -}) + test("...", () => {}); +}); ``` ### 4. 快照测试 + 通常在测试 UI 组件时,我们会建议进行快照测试,以确保 UI 不会有意外的改变。这里我们建议使用 `react-test-renderer` 进行快照测试。 ```shell yarn add react-test-renderer @types/react-test-renderer -D ``` + > 安装完成后,建议在 UI 测试的首个测试用例进行快照测试。 ```ts -import React from 'react'; -import renderer from 'react-test-renderer'; -import { Toolbar } from '..'; - -test('Match Snapshot', () => { -const component = renderer.create(); -const toolbar = component.toJSON(); - expect(toolbar).toMatchSnapshot(); +import React from "react"; +import renderer from "react-test-renderer"; +import { Toolbar } from ".."; + +test("Match Snapshot", () => { + const component = renderer.create(); + const toolbar = component.toJSON(); + expect(toolbar).toMatchSnapshot(); }); ``` ### 5. 函数命名 + 关于是使用 `test` 还是使用 `it` 的争论,我们不做限制。但是建议一个项目里,尽量保持风格一致,如果其余测试用例中均为 `test`,则建议保持统一。 + ### 6. 业务代码 + 我们建议尽量把业务代码的函数的功能单一化,简单化。如果一个函数的功能包含了十几个功能数十个功能,那我们建议对该函数进行拆分,从而更加有利于测试的进行 + ### 7. 重构代码 + 在重构代码之前,请确保该模块的测试用例已经补全,否则重构代码的风险会过于巨大,从而导致无法控制开发成本。 + ### 8. 覆盖率 -我们建议尽量以覆盖率 **100% **为目标。当然,在具体的开发过程中会有各种各样的情况,所以很少有能够达到 100% 的情况出现。 + +我们建议尽量以覆盖率 **100%** 为目标。当然,在具体的开发过程中会有各种各样的情况,所以很少有能够达到 100% 的情况出现。 + ### 9. 修复问题 + 每当我们修复了一个 bug,我们应当评估是否有必要为这个 bug 添加一个测试用例。如果需要的话,则在测试用例中新增一条以确保后续的开发中不会复现该 bug。 评估的参考内容如下: @@ -59,24 +76,29 @@ const toolbar = component.toJSON(); - 是否会影响内容的展示 以上内容,满足一条或多条,则认为应当为该 bug 新增测试用例。 + ### 10. toBe or toEqual + 这两者的区别在于,`toBe` 是相等,即 `===`,而 `toEqual` 是内容相同,即深度相等。我们建议基础类型用 `toBe`,复杂类型用 `toEqual`。 ### 11. Drag + 有时候,我们需要去测试拖拽功能,我们建议用以下函数来执行模拟拖拽的操作 ```javascript -import { fireEvent } from '@testing-library/react'; +import { fireEvent } from "@testing-library/react"; function dragToTargetNode(source: HTMLElement, target: HTMLElement) { - fireEvent.dragStart(source); - fireEvent.dragOver(target); - fireEvent.drop(target); - fireEvent.dragEnd(source); + fireEvent.dragStart(source); + fireEvent.dragOver(target); + fireEvent.drop(target); + fireEvent.dragEnd(source); } ``` + ### 12. UI 测试 -除了快照测试外,UI 测试还需要测试 UI 组件是否如期望般渲染,那么我们建议使用 `[@testing-library/jest-dom](https://github.com/testing-library/jest-dom)` 做相关的测试 + +除了快照测试外,UI 测试还需要测试 UI 组件是否如期望般渲染,那么我们建议使用 [`@testing-library/jest-dom`](https://github.com/testing-library/jest-dom) 做相关的测试 ```shell yarn add --dev @testing-library/jest-dom @@ -111,11 +133,13 @@ describe('Test Breadcrumb Component', () => { ``` 除了 `toBeInTheDocument` 外,还有其余接口,参见官方文档。 + ### 13. `test.only` -在出现测试用例无法通过,但是又判断代码的**逻辑**没有问题之后,将该条测试用例设置为 `only` 再跑一遍测试用例,以确保不是其他测试用例导致的该测试用例的失败。这类问题经常出现自代码中欠缺深拷贝,导致多条测试用例之中修改了原数据从而使得数据不匹配。 + +在出现测试用例无法通过,但是又判断代码的**逻辑**没有问题之后,将该条测试用例设置为 `only` 再跑一遍测试用例,以确保不是其他测试用例导致的该测试用例的失败。这类问题经常出现在代码中欠缺深拷贝,导致多条测试用例之中修改了原数据从而使得数据不匹配。 例如: -```ts +```diff // mycode.ts function add(record: Record){ Object.assign(record, { flag: false}); @@ -123,14 +147,9 @@ function add(record: Record){ // mycode.test.ts const mockData = {}; -test('',() => { -add(mockData) - // ... - // ... -}) - -test.only('',() => { -add(mockData) // the mockData is modified by add function here +- test('',() => { ++ test.only('',() => { + expect(add(mockData)).toEqual({ flag: false }); // ... // ... })