@teambit/tester 1.0.108 → 1.0.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/preview/teambit_defender_tester-preview.js +1 -0
- package/dist/{preview-1703647408454.js → preview-1703698405864.js} +2 -2
- package/package.json +14 -14
- package/index.ts +0 -18
- package/tester-env.ts +0 -6
- package/tester.aspect.ts +0 -7
- package/tester.graphql.ts +0 -80
- package/tester.main.runtime.ts +0 -268
- package/tester.task.ts +0 -116
- package/tester.ts +0 -172
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["teambit.defender/tester-preview"]=t():e["teambit.defender/tester-preview"]=t()}(self,(()=>(()=>{"use strict";var e={6723:(e,t,n)=>{var r={id:"teambit.defender/tester@1.0.109",homepage:"https://bit.cloud/teambit/defender/tester",exported:!0};function o(){const e=i(n(87363));return o=function(){return e},e}function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0}),t.Logo=void 0,o.__bit_component=r,i.__bit_component=r;const a=()=>o().default.createElement("div",{style:{height:"100%",display:"flex",justifyContent:"center"}},o().default.createElement("img",{style:{width:70},src:"https://static.bit.dev/extensions-icons/tester.svg"}));a.__bit_component=r,t.Logo=a},878:(e,t,n)=>{Object.defineProperty(t,"ZP",{enumerable:!0,get:function(){return r.default}});var r=o(n(22280));function o(e){return e&&e.__esModule?e:{default:e}}o.__bit_component={id:"teambit.defender/content/tester-overview@1.95.0",homepage:"https://bit.dev/teambit/defender/content/tester-overview",exported:!0}},22280:(e,t,n)=>{var r={id:"teambit.defender/content/tester-overview@1.95.0",homepage:"https://bit.dev/teambit/defender/content/tester-overview",exported:!0};Object.defineProperty(t,"__esModule",{value:!0}),t.default=u,s(n(87363));var o=n(40040),i=n(70500),a=["components"];function s(e){return e&&e.__esModule?e:{default:e}}function p(){return p=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},p.apply(this,arguments)}function d(e,t){if(null==e)return{};var n,r,o=l(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function l(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}s.__bit_component=r,p.__bit_component=r,d.__bit_component=r,l.__bit_component=r;var m={},c="wrapper";function u(e){var t=e.components,n=d(e,a);return(0,o.mdx)(c,p({},m,n,{components:t,mdxType:"MDXLayout"}),(0,o.mdx)(i.MDXScopeProvider,{components:{},mdxType:"MDXScopeProvider"},(0,o.mdx)("h1",null,"Tester Overview"),(0,o.mdx)("p",null,"The ",(0,o.mdx)("em",{parentName:"p"},"Tester")," provides a unified interface for testing components regardless of the specific tester they use."),(0,o.mdx)("p",null,"Software testing is a way to validate that an application or a module of it, behaves as expected."),(0,o.mdx)("p",null,"Web applications and services are often tested using a set of testing tools, where each tool plays a different role. For example, ",(0,o.mdx)("a",{parentName:"p",href:"https://jestjs.io/"},"Jest")," can be used as a test runner and as an assertion library, while ",(0,o.mdx)("a",{parentName:"p",href:"https://testing-library.com/"},"React Testing Library")," can be used for testing utilities."),(0,o.mdx)("p",null,"The Tester aspect is responsible for standardizing test execution for components; That is, it standardizes the way test runners are used. Other testing libraries used for utils, mocks, etc., are irrelevant, in that sense."),(0,o.mdx)("p",null,"Standardization is done for component testing during development (in the workspace) as well as for distribution (in a capsule ",", as part of the component ",(0,o.mdx)("a",{parentName:"p",href:"/builder/build-pipelines"},"build pipeline"),")."),(0,o.mdx)("h2",null,"Testing in development"),(0,o.mdx)("p",null,"Bit tests all components in the ",(0,o.mdx)("a",{parentName:"p",href:"/workspace/overview"},"Workspace")," using just a single command. That is true for components of all types, regardless of their specific ",(0,o.mdx)("a",{parentName:"p",href:"/envs/overview"},"Env"),", and as a consequence of that, their specific ",(0,o.mdx)("a",{parentName:"p",href:"/tester/overview"},"Tester implementation")," and configuration."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-bash"},"bit test\n")),(0,o.mdx)("p",null,"Components can be watched for changes and retested with a single command, as well."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-bash"},"bit test -w\n")),(0,o.mdx)("p",null,"To learn more on testing during development, please refer to ",(0,o.mdx)("a",{parentName:"p",href:"/tester/workspace-testing"},"Workspace Testing"),"."),(0,o.mdx)("h2",null,"Testing during build"),(0,o.mdx)("p",null,"Testing components for distribution is done during ",(0,o.mdx)("inlineCode",{parentName:"p"},"build")," by the Bit ",(0,o.mdx)("a",{parentName:"p",href:"/builder/overview"},"Builder"),"."),(0,o.mdx)("p",null,"Component build can be simulated with ",(0,o.mdx)("inlineCode",{parentName:"p"},"bit build")," and done through ",(0,o.mdx)("a",{parentName:"p",href:"/components/tags"},"Tag")," or ",(0,o.mdx)("a",{parentName:"p",href:"/components/snaps"},"Snap")),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-bash"},"bit build\n")),(0,o.mdx)("p",null,"By default, Bit's pre-configured Envs includes a ",(0,o.mdx)("a",{parentName:"p",href:"/builder/overview#build-task"},"Build Task")," for testing components in the ",(0,o.mdx)("a",{parentName:"p",href:"/builder/build-pipelines"},"Build Pipeline"),", each using the Env's specific test configs."),(0,o.mdx)("p",null,"Testing during build provides the highest \"external validity\" by running tests on the distributable code that was generated by the Env's compiler and not by the tester's.\nThat way, the exact same code that will be consumed by other components, is tested before it is released. Moreover, since each component is tested in a capsule ",", isolated from the rest of the workspace, tests are guaranteed to provide results that are context-agnostic and free of false-positives."),(0,o.mdx)("h2",null,"Configuring and implementing Testers"),(0,o.mdx)("p",null,"Testers are configured in the ",(0,o.mdx)("a",{parentName:"p",href:"/envs/overview"},"Env")," which is configured on your ",(0,o.mdx)("a",{parentName:"p",href:"/components/overview"},"Component"),". Customizing the Tester can be done by ",(0,o.mdx)("a",{parentName:"p",href:"/envs/services/testing"},"customizing an existing env with your tester")," or by ",(0,o.mdx)("a",{parentName:"p",href:"/envs/customizing-env"},"implementing your own Env"),"."),(0,o.mdx)("p",null,"Tester can also be implemented into Bit through few interfaces. \x3c!--TODO For more information on implementing your own compiler please refer to ",(0,o.mdx)("a",{parentName:"p",href:"/"},"Implement a Compiler"),".")))}u.__bit_component=r,u.isMDXComponent=!0},40040:e=>{e.exports=MdxJsReact},87363:e=>{e.exports=React},70500:e=>{e.exports=TeambitMdxUiMdxScopeContext}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={exports:{}};return e[r](i,i.exports,n),i.exports}n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var r={};return(()=>{n.r(r),n.d(r,{compositions:()=>c,compositions_metadata:()=>f,overview:()=>u});var e={};n.r(e),n.d(e,{default:()=>m});var t=n(6723),o=(n(87363),n(40040)),i=n(70500),a=n(878),s=["components"];function p(){return p=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},p.apply(this,arguments)}var d={},l="wrapper";function m(e){var t=e.components,n=function(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,s);return(0,o.mdx)(l,p({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.mdx)(i.MDXScopeProvider,{components:{TesterOverview:a.ZP},mdxType:"MDXScopeProvider"},(0,o.mdx)(a.ZP,{mdxType:"TesterOverview"})))}m.isMDXComponent=!0;const c=[t],u=[e],f={compositions:[{displayName:"Logo",identifier:"Logo"}]}})(),r})()));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as compositions_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.defender_tester@1.0.
|
|
2
|
-
import * as overview_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.defender_tester@1.0.
|
|
1
|
+
import * as compositions_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.defender_tester@1.0.109/dist/tester.composition.js';
|
|
2
|
+
import * as overview_0 from '/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.defender_tester@1.0.109/dist/tester.docs.mdx';
|
|
3
3
|
|
|
4
4
|
export const compositions = [compositions_0];
|
|
5
5
|
export const overview = [overview_0];
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teambit/tester",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.109",
|
|
4
4
|
"homepage": "https://bit.cloud/teambit/defender/tester",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"componentId": {
|
|
7
7
|
"scope": "teambit.defender",
|
|
8
8
|
"name": "tester",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.109"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"chalk": "2.4.2",
|
|
@@ -26,17 +26,17 @@
|
|
|
26
26
|
"@teambit/defender.ui.test-compare": "0.0.255",
|
|
27
27
|
"@teambit/defender.ui.test-page": "0.0.34",
|
|
28
28
|
"@teambit/bit-error": "0.0.404",
|
|
29
|
-
"@teambit/cli": "0.0.
|
|
30
|
-
"@teambit/logger": "0.0.
|
|
31
|
-
"@teambit/workspace": "1.0.
|
|
32
|
-
"@teambit/envs": "1.0.
|
|
33
|
-
"@teambit/component": "1.0.
|
|
34
|
-
"@teambit/graphql": "1.0.
|
|
35
|
-
"@teambit/builder": "1.0.
|
|
36
|
-
"@teambit/dev-files": "1.0.
|
|
37
|
-
"@teambit/ui": "1.0.
|
|
38
|
-
"@teambit/compiler": "1.0.
|
|
39
|
-
"@teambit/component-compare": "1.0.
|
|
29
|
+
"@teambit/cli": "0.0.841",
|
|
30
|
+
"@teambit/logger": "0.0.934",
|
|
31
|
+
"@teambit/workspace": "1.0.109",
|
|
32
|
+
"@teambit/envs": "1.0.109",
|
|
33
|
+
"@teambit/component": "1.0.109",
|
|
34
|
+
"@teambit/graphql": "1.0.109",
|
|
35
|
+
"@teambit/builder": "1.0.109",
|
|
36
|
+
"@teambit/dev-files": "1.0.109",
|
|
37
|
+
"@teambit/ui": "1.0.109",
|
|
38
|
+
"@teambit/compiler": "1.0.109",
|
|
39
|
+
"@teambit/component-compare": "1.0.109"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/fs-extra": "9.0.7",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@types/jest": "^29.2.2",
|
|
47
47
|
"@types/testing-library__jest-dom": "^5.9.5",
|
|
48
48
|
"@teambit/defender.content.tester-overview": "1.95.0",
|
|
49
|
-
"@teambit/harmony.envs.core-aspect-env": "0.0.
|
|
49
|
+
"@teambit/harmony.envs.core-aspect-env": "0.0.14"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": "^17.0.0 || ^18.0.0",
|
package/index.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { TesterAspect } from './tester.aspect';
|
|
2
|
-
|
|
3
|
-
export { Tests } from './tester';
|
|
4
|
-
export type { TesterEnv } from './tester-env';
|
|
5
|
-
export type {
|
|
6
|
-
Tester,
|
|
7
|
-
TesterContext,
|
|
8
|
-
CallbackFn,
|
|
9
|
-
SpecFiles,
|
|
10
|
-
ComponentPatternsMap,
|
|
11
|
-
ComponentsResults,
|
|
12
|
-
ComponentPatternsEntry,
|
|
13
|
-
} from './tester';
|
|
14
|
-
export type { TesterMain } from './tester.main.runtime';
|
|
15
|
-
export type { TesterUI, EmptyStateSlot } from './tester.ui.runtime';
|
|
16
|
-
|
|
17
|
-
export { TesterAspect };
|
|
18
|
-
export default TesterAspect;
|
package/tester-env.ts
DELETED
package/tester.aspect.ts
DELETED
package/tester.graphql.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { GraphqlMain, Schema } from '@teambit/graphql';
|
|
2
|
-
import { ComponentFactory } from '@teambit/component';
|
|
3
|
-
import { withFilter } from 'graphql-subscriptions';
|
|
4
|
-
import { ComponentID } from '@teambit/component-id';
|
|
5
|
-
import gql from 'graphql-tag';
|
|
6
|
-
|
|
7
|
-
import { TesterMain } from './tester.main.runtime';
|
|
8
|
-
import { OnTestsChanged } from './tester.service';
|
|
9
|
-
|
|
10
|
-
export function testerSchema(tester: TesterMain, graphql: GraphqlMain): Schema {
|
|
11
|
-
return {
|
|
12
|
-
typeDefs: gql`
|
|
13
|
-
extend type ComponentHost {
|
|
14
|
-
getTests(id: String!): Tests
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type Subscription {
|
|
18
|
-
testsChanged(id: String!): Tests
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
type Tests {
|
|
22
|
-
loading: Boolean!
|
|
23
|
-
testsResults: TestsResults
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
type TestsChanged {
|
|
27
|
-
testsResults: TestsResults
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type TestsResults {
|
|
31
|
-
testFiles: [TestFiles]
|
|
32
|
-
success: Boolean
|
|
33
|
-
start: Int
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
type TestFiles {
|
|
37
|
-
file: String
|
|
38
|
-
tests: [Tests]
|
|
39
|
-
pass: Int
|
|
40
|
-
failed: Int
|
|
41
|
-
pending: Int
|
|
42
|
-
duration: Int
|
|
43
|
-
slow: Boolean
|
|
44
|
-
errorStr: String
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
type Tests {
|
|
48
|
-
ancestor: [String]
|
|
49
|
-
name: String
|
|
50
|
-
duration: String
|
|
51
|
-
status: String
|
|
52
|
-
error: String
|
|
53
|
-
}
|
|
54
|
-
`,
|
|
55
|
-
resolvers: {
|
|
56
|
-
Subscription: {
|
|
57
|
-
testsChanged: {
|
|
58
|
-
subscribe: withFilter(
|
|
59
|
-
() => graphql.pubsub.asyncIterator(OnTestsChanged),
|
|
60
|
-
(payload, variables) => {
|
|
61
|
-
return payload.testsChanged.id === variables.id;
|
|
62
|
-
}
|
|
63
|
-
),
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
ComponentHost: {
|
|
68
|
-
getTests: async (host: ComponentFactory, { id }: { id: string }) => {
|
|
69
|
-
const componentId = await host.resolveComponentId(id);
|
|
70
|
-
const idHasVersion = ComponentID.fromString(id).hasVersion();
|
|
71
|
-
const component = await host.get(componentId);
|
|
72
|
-
if (!component) return null;
|
|
73
|
-
const testsResults = await tester.getTestsResults(component, idHasVersion);
|
|
74
|
-
if (!testsResults) return null;
|
|
75
|
-
return testsResults;
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
}
|
package/tester.main.runtime.ts
DELETED
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import { CLIAspect, CLIMain, MainRuntime } from '@teambit/cli';
|
|
3
|
-
import { Component, IComponent } from '@teambit/component';
|
|
4
|
-
import compact from 'lodash.compact';
|
|
5
|
-
import { EnvsAspect, EnvsExecutionResult, EnvsMain } from '@teambit/envs';
|
|
6
|
-
import { LoggerAspect, LoggerMain } from '@teambit/logger';
|
|
7
|
-
import { Workspace, WorkspaceAspect } from '@teambit/workspace';
|
|
8
|
-
import { GraphqlAspect, GraphqlMain } from '@teambit/graphql';
|
|
9
|
-
import { BuilderAspect, BuilderMain } from '@teambit/builder';
|
|
10
|
-
import { UiMain, UIAspect } from '@teambit/ui';
|
|
11
|
-
import { merge } from 'lodash';
|
|
12
|
-
import DevFilesAspect, { DevFilesMain } from '@teambit/dev-files';
|
|
13
|
-
import { TestsResult } from '@teambit/tests-results';
|
|
14
|
-
import { ComponentsResults, CallbackFn, Tests } from './tester';
|
|
15
|
-
import { TestCmd } from './test.cmd';
|
|
16
|
-
import { TesterAspect } from './tester.aspect';
|
|
17
|
-
import { TesterService } from './tester.service';
|
|
18
|
-
import { TesterTask } from './tester.task';
|
|
19
|
-
import { detectTestFiles } from './utils';
|
|
20
|
-
import { testerSchema } from './tester.graphql';
|
|
21
|
-
import { testsResultsToJUnitFormat } from './utils/junit-generator';
|
|
22
|
-
|
|
23
|
-
export type TesterExtensionConfig = {
|
|
24
|
-
/**
|
|
25
|
-
* regex of the text environment.
|
|
26
|
-
*/
|
|
27
|
-
testRegex: string;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* determine whether to watch on start.
|
|
31
|
-
*/
|
|
32
|
-
watchOnStart: boolean;
|
|
33
|
-
patterns: string[];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export type TesterOptions = {
|
|
37
|
-
/**
|
|
38
|
-
* start the tester in watch mode.
|
|
39
|
-
*/
|
|
40
|
-
watch: boolean;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* start the tester in debug mode.
|
|
44
|
-
*/
|
|
45
|
-
debug: boolean;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* start the tester in debug mode.
|
|
49
|
-
*/
|
|
50
|
-
ui?: boolean;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* initiate the tester on given env.
|
|
54
|
-
*/
|
|
55
|
-
env?: string;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* generate JUnit files on the specified dir
|
|
59
|
-
*/
|
|
60
|
-
junit?: string;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* show code coverage
|
|
64
|
-
*/
|
|
65
|
-
coverage?: boolean;
|
|
66
|
-
|
|
67
|
-
callback?: CallbackFn;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export class TesterMain {
|
|
71
|
-
static runtime = MainRuntime;
|
|
72
|
-
static dependencies = [
|
|
73
|
-
CLIAspect,
|
|
74
|
-
EnvsAspect,
|
|
75
|
-
WorkspaceAspect,
|
|
76
|
-
LoggerAspect,
|
|
77
|
-
GraphqlAspect,
|
|
78
|
-
UIAspect,
|
|
79
|
-
DevFilesAspect,
|
|
80
|
-
BuilderAspect,
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
constructor(
|
|
84
|
-
private patterns: string[],
|
|
85
|
-
/**
|
|
86
|
-
* graphql extension.
|
|
87
|
-
*/
|
|
88
|
-
private graphql: GraphqlMain,
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* envs extension.
|
|
92
|
-
*/
|
|
93
|
-
private envs: EnvsMain,
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* workspace extension.
|
|
97
|
-
*/
|
|
98
|
-
private workspace: Workspace,
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* tester service.
|
|
102
|
-
*/
|
|
103
|
-
readonly service: TesterService,
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* build task.
|
|
107
|
-
*/
|
|
108
|
-
readonly task: TesterTask,
|
|
109
|
-
|
|
110
|
-
private devFiles: DevFilesMain,
|
|
111
|
-
|
|
112
|
-
private builder: BuilderMain
|
|
113
|
-
) {}
|
|
114
|
-
|
|
115
|
-
_testsResults: { [componentId: string]: ComponentsResults } | undefined[] = [];
|
|
116
|
-
|
|
117
|
-
async test(components: Component[], opts?: TesterOptions): Promise<EnvsExecutionResult<Tests>> {
|
|
118
|
-
const options = this.getOptions(opts);
|
|
119
|
-
const envsRuntime = await this.envs.createEnvironment(components);
|
|
120
|
-
if (opts?.env) {
|
|
121
|
-
return envsRuntime.runEnv(opts.env, this.service, options);
|
|
122
|
-
}
|
|
123
|
-
const results = await envsRuntime.run(this.service, options);
|
|
124
|
-
if (opts?.junit) {
|
|
125
|
-
await this.generateJUnit(opts?.junit, results);
|
|
126
|
-
}
|
|
127
|
-
return results;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private async generateJUnit(filePath: string, testsResults: EnvsExecutionResult<Tests>) {
|
|
131
|
-
const components = testsResults.results.map((envResult) => envResult.data?.components).flat();
|
|
132
|
-
const jUnit = testsResultsToJUnitFormat(compact(components));
|
|
133
|
-
await fs.outputFile(filePath, jUnit);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* watch all components for changes and test upon each.
|
|
138
|
-
*/
|
|
139
|
-
async watch(components: Component[], opts?: TesterOptions) {
|
|
140
|
-
const options = this.getOptions(opts);
|
|
141
|
-
const envsRuntime = await this.envs.createEnvironment(components);
|
|
142
|
-
if (opts?.env) {
|
|
143
|
-
return envsRuntime.runEnv(opts.env, this.service, options);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
this.service.onTestRunComplete((results) => {
|
|
147
|
-
results.components.forEach((component) => {
|
|
148
|
-
this._testsResults[component.componentId.toString()] = component;
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
return envsRuntime.run(this.service, options);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async uiWatch() {
|
|
155
|
-
const components = await this.workspace.list();
|
|
156
|
-
return this.watch(components, { watch: true, debug: false, ui: true });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async getTestsResults(
|
|
160
|
-
component: IComponent,
|
|
161
|
-
idHasVersion = true
|
|
162
|
-
): Promise<{ testsResults?: TestsResult; loading: boolean } | undefined> {
|
|
163
|
-
const entry = component.get(TesterAspect.id);
|
|
164
|
-
const isModified = !idHasVersion && (await component.isModified());
|
|
165
|
-
const data = this.builder.getDataByAspect(component, TesterAspect.id) as { tests: TestsResult };
|
|
166
|
-
if ((entry || data) && !isModified) {
|
|
167
|
-
return { testsResults: data?.tests || entry?.data.tests, loading: false };
|
|
168
|
-
}
|
|
169
|
-
return this.getTestsResultsFromState(component);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private getTestsResultsFromState(component: IComponent) {
|
|
173
|
-
const tests = this._testsResults[component.id.toString()];
|
|
174
|
-
return { testsResults: tests?.results, loading: tests?.loading || false };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Get the tests patterns from the config. (used as default patterns in case the env does not provide them via getTestsDevPatterns)
|
|
179
|
-
* @returns
|
|
180
|
-
*/
|
|
181
|
-
getPatterns() {
|
|
182
|
-
return this.patterns;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
getComponentDevPatterns(component: Component) {
|
|
186
|
-
const env = this.envs.calculateEnv(component, { skipWarnings: !!this.workspace?.inInstallContext }).env;
|
|
187
|
-
const componentPatterns: string[] = env.getTestsDevPatterns
|
|
188
|
-
? env.getTestsDevPatterns(component)
|
|
189
|
-
: this.getPatterns();
|
|
190
|
-
return { name: 'tests', pattern: componentPatterns };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
getDevPatternToRegister() {
|
|
194
|
-
return this.getComponentDevPatterns.bind(this);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* get all test files of a component.
|
|
199
|
-
*/
|
|
200
|
-
getTestFiles(component: Component) {
|
|
201
|
-
return detectTestFiles(component, this.devFiles);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private getOptions(options?: TesterOptions): TesterOptions {
|
|
205
|
-
const defaults = {
|
|
206
|
-
watch: false,
|
|
207
|
-
debug: false,
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
return merge(defaults, options);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
static defaultConfig = {
|
|
214
|
-
/**
|
|
215
|
-
* default test regex for which files tester to apply on.
|
|
216
|
-
*/
|
|
217
|
-
patterns: ['**/*.spec.+(js|ts|jsx|tsx)', '**/*.test.+(js|ts|jsx|tsx)'],
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* determine whether to watch on start.
|
|
221
|
-
*/
|
|
222
|
-
watchOnStart: false,
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
static async provider(
|
|
226
|
-
[cli, envs, workspace, loggerAspect, graphql, ui, devFiles, builder]: [
|
|
227
|
-
CLIMain,
|
|
228
|
-
EnvsMain,
|
|
229
|
-
Workspace,
|
|
230
|
-
LoggerMain,
|
|
231
|
-
GraphqlMain,
|
|
232
|
-
UiMain,
|
|
233
|
-
DevFilesMain,
|
|
234
|
-
BuilderMain
|
|
235
|
-
],
|
|
236
|
-
config: TesterExtensionConfig
|
|
237
|
-
) {
|
|
238
|
-
const logger = loggerAspect.createLogger(TesterAspect.id);
|
|
239
|
-
const testerService = new TesterService(workspace, logger, graphql.pubsub, devFiles);
|
|
240
|
-
envs.registerService(testerService);
|
|
241
|
-
const tester = new TesterMain(
|
|
242
|
-
config.patterns,
|
|
243
|
-
graphql,
|
|
244
|
-
envs,
|
|
245
|
-
workspace,
|
|
246
|
-
testerService,
|
|
247
|
-
new TesterTask(TesterAspect.id, devFiles),
|
|
248
|
-
devFiles,
|
|
249
|
-
builder
|
|
250
|
-
);
|
|
251
|
-
devFiles.registerDevPattern(tester.getDevPatternToRegister());
|
|
252
|
-
|
|
253
|
-
if (workspace) {
|
|
254
|
-
ui.registerOnStart(async () => {
|
|
255
|
-
if (!config.watchOnStart) return undefined;
|
|
256
|
-
await tester.uiWatch();
|
|
257
|
-
return undefined;
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
cli.register(new TestCmd(tester, workspace, logger));
|
|
261
|
-
|
|
262
|
-
graphql.register(testerSchema(tester, graphql));
|
|
263
|
-
|
|
264
|
-
return tester;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
TesterAspect.addRuntime(TesterMain);
|
package/tester.task.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { BuildContext, BuiltTaskResult, BuildTask, CAPSULE_ARTIFACTS_DIR } from '@teambit/builder';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { Compiler, CompilerAspect } from '@teambit/compiler';
|
|
5
|
-
import { DevFilesMain } from '@teambit/dev-files';
|
|
6
|
-
import { ComponentMap } from '@teambit/component';
|
|
7
|
-
import { Tester } from './tester';
|
|
8
|
-
import { detectTestFiles } from './utils';
|
|
9
|
-
import { testsResultsToJUnitFormat } from './utils/junit-generator';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* tester build task. Allows to test components during component build.
|
|
13
|
-
*/
|
|
14
|
-
export class TesterTask implements BuildTask {
|
|
15
|
-
readonly name = 'TestComponents';
|
|
16
|
-
readonly dependencies = [CompilerAspect.id];
|
|
17
|
-
constructor(readonly aspectId: string, private devFiles: DevFilesMain) {}
|
|
18
|
-
|
|
19
|
-
async execute(context: BuildContext): Promise<BuiltTaskResult> {
|
|
20
|
-
const components = context.capsuleNetwork.originalSeedersCapsules.getAllComponents();
|
|
21
|
-
const tester: Tester = context.env.getTester();
|
|
22
|
-
const componentsSpecFiles = ComponentMap.as(components, (component) => {
|
|
23
|
-
return detectTestFiles(component, this.devFiles);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const testCount = componentsSpecFiles.toArray().reduce((acc, [, specs]) => acc + specs.length, 0);
|
|
27
|
-
if (testCount === 0)
|
|
28
|
-
return {
|
|
29
|
-
artifacts: [],
|
|
30
|
-
componentsResults: [],
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const patternsWithCapsule = ComponentMap.as(components, (component) => {
|
|
34
|
-
const componentSpecFiles = componentsSpecFiles.get(component);
|
|
35
|
-
if (!componentSpecFiles) throw new Error('capsule not found');
|
|
36
|
-
const [, specs] = componentSpecFiles;
|
|
37
|
-
const capsule = context.capsuleNetwork.graphCapsules.getCapsule(component.id);
|
|
38
|
-
if (!capsule) throw new Error('capsule not found');
|
|
39
|
-
const compiler: Compiler = context.env.getCompiler();
|
|
40
|
-
if (!compiler) {
|
|
41
|
-
throw new Error(`compiler not found for ${component.id.toString()}`);
|
|
42
|
-
}
|
|
43
|
-
// @ts-ignore. not sure why ts complain that compiler might be undefined, when we check it above.
|
|
44
|
-
const distFolder = compiler.getDistDir() || compiler.distDir;
|
|
45
|
-
return {
|
|
46
|
-
componentDir: join(capsule.path, distFolder),
|
|
47
|
-
paths: specs.map((specFile) => {
|
|
48
|
-
const distPath = compiler.getDistPathBySrcPath(specFile.relative);
|
|
49
|
-
// TODO: fix spec type file need to capsule will return files with type AbstractVinyl
|
|
50
|
-
return { path: join(capsule.path, distPath), relative: distPath };
|
|
51
|
-
}),
|
|
52
|
-
};
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const specFilesWithCapsule = ComponentMap.as(components, (component) => {
|
|
56
|
-
const patternEntry = patternsWithCapsule.get(component);
|
|
57
|
-
// @ts-ignore
|
|
58
|
-
const [, val] = patternEntry;
|
|
59
|
-
return val.paths;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const testerContext = Object.assign(context, {
|
|
63
|
-
release: true,
|
|
64
|
-
specFiles: specFilesWithCapsule,
|
|
65
|
-
rootPath: context.capsuleNetwork.capsulesRootDir,
|
|
66
|
-
patterns: patternsWithCapsule,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// TODO: remove after fix AbstractVinyl on capsule
|
|
70
|
-
// @ts-ignore
|
|
71
|
-
const testsResults = await tester.test(testerContext);
|
|
72
|
-
|
|
73
|
-
// write junit files
|
|
74
|
-
await Promise.all(
|
|
75
|
-
testsResults.components.map(async (compResult) => {
|
|
76
|
-
const junit = testsResultsToJUnitFormat([compResult]);
|
|
77
|
-
const capsule = context.capsuleNetwork.graphCapsules.getCapsule(compResult.componentId);
|
|
78
|
-
if (!capsule) {
|
|
79
|
-
throw new Error(`unable to find ${compResult.componentId.toString()} in capsules`);
|
|
80
|
-
}
|
|
81
|
-
await fs.outputFile(join(capsule.path, getJUnitArtifactPath()), junit);
|
|
82
|
-
})
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
artifacts: getArtifactDef(), // @ts-ignore
|
|
87
|
-
componentsResults: testsResults.components.map((componentTests) => {
|
|
88
|
-
const componentErrors = componentTests.errors;
|
|
89
|
-
const component = context.capsuleNetwork.graphCapsules.getCapsule(componentTests.componentId)?.component;
|
|
90
|
-
if (!component) {
|
|
91
|
-
throw new Error(`unable to find ${componentTests.componentId.toString()} in capsules`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
component,
|
|
96
|
-
metadata: { tests: componentTests.results },
|
|
97
|
-
errors: componentErrors,
|
|
98
|
-
};
|
|
99
|
-
}),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function getJUnitArtifactPath() {
|
|
105
|
-
return join(CAPSULE_ARTIFACTS_DIR, '__bit_junit.xml');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function getArtifactDef() {
|
|
109
|
-
return [
|
|
110
|
-
{
|
|
111
|
-
name: 'junit',
|
|
112
|
-
globPatterns: [getJUnitArtifactPath()],
|
|
113
|
-
rootDir: CAPSULE_ARTIFACTS_DIR,
|
|
114
|
-
},
|
|
115
|
-
];
|
|
116
|
-
}
|
package/tester.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { Component, ComponentID, ComponentMap } from '@teambit/component';
|
|
2
|
-
import { ExecutionContext } from '@teambit/envs';
|
|
3
|
-
import { AbstractVinyl } from '@teambit/legacy/dist/consumer/component/sources';
|
|
4
|
-
import { TestsResult } from '@teambit/tests-results';
|
|
5
|
-
|
|
6
|
-
export class Tests {
|
|
7
|
-
constructor(public components: ComponentsResults[]) {}
|
|
8
|
-
get errors(): Error[] {
|
|
9
|
-
return this.components.map((comp) => comp.errors || []).flat();
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type ComponentsResults = {
|
|
14
|
-
/**
|
|
15
|
-
* component id.
|
|
16
|
-
*/
|
|
17
|
-
componentId: ComponentID;
|
|
18
|
-
/**
|
|
19
|
-
* test results for the component.
|
|
20
|
-
*/
|
|
21
|
-
results?: TestsResult;
|
|
22
|
-
/**
|
|
23
|
-
* aggregated errors from all files
|
|
24
|
-
*/
|
|
25
|
-
errors?: Error[];
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* loading.
|
|
29
|
-
*/
|
|
30
|
-
loading?: boolean;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type SpecFiles = ComponentMap<AbstractVinyl[]>;
|
|
34
|
-
|
|
35
|
-
export type ComponentPatternsEntry = {
|
|
36
|
-
/**
|
|
37
|
-
* component directory in the workspace.
|
|
38
|
-
*/
|
|
39
|
-
componentDir: string;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* paths to test files.
|
|
43
|
-
*/
|
|
44
|
-
paths: { path: string; relative: string }[];
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* root dir of the package in bit_roots.
|
|
48
|
-
*/
|
|
49
|
-
packageRootDir: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type ComponentPatternsMap = ComponentMap<ComponentPatternsEntry>;
|
|
53
|
-
|
|
54
|
-
export interface TesterContext extends ExecutionContext {
|
|
55
|
-
/**
|
|
56
|
-
* whether the tester run for release (during bit build/tag) or not (during bit test command).
|
|
57
|
-
*/
|
|
58
|
-
release: boolean;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* list of components to test.
|
|
62
|
-
*/
|
|
63
|
-
components: Component[];
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* component workspace.
|
|
67
|
-
*/
|
|
68
|
-
// workspace: Workspace;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* defines whether tester is expected to run in quiet mode.
|
|
72
|
-
*/
|
|
73
|
-
quiet?: boolean;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* list of spec files to test.
|
|
77
|
-
*/
|
|
78
|
-
specFiles: SpecFiles;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* rootPath of the component workspace or the capsule root dir (during build).
|
|
82
|
-
*/
|
|
83
|
-
rootPath: string;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* determines whether tester is expected to run in debug mode.
|
|
87
|
-
*/
|
|
88
|
-
debug?: boolean;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* is start from ui
|
|
92
|
-
*/
|
|
93
|
-
ui?: boolean;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* determines whether to start the tester in watch mode.
|
|
97
|
-
*/
|
|
98
|
-
watch?: boolean;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* whether the tester should show code coverage
|
|
102
|
-
*/
|
|
103
|
-
coverage?: boolean;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* array of patterns to test.
|
|
107
|
-
*/
|
|
108
|
-
patterns: ComponentPatternsMap;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
*
|
|
112
|
-
* additional test host dependencies
|
|
113
|
-
* This can be used in cases when you want specific dependencies to be resolved from the env during testing
|
|
114
|
-
* but you don't want these dependencies as peer dependencies of the component (as they are not used during runtime)
|
|
115
|
-
* An example for this is @angular/compiler, which during running tests you want to resolve from the env, but you don't
|
|
116
|
-
* need it during component runtime.
|
|
117
|
-
*/
|
|
118
|
-
additionalHostDependencies?: string[];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* tester interface allows extensions to implement a component tester into bit.
|
|
123
|
-
*/
|
|
124
|
-
export interface Tester {
|
|
125
|
-
/**
|
|
126
|
-
* display name of the tester.
|
|
127
|
-
*/
|
|
128
|
-
displayName?: string;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* icon of the tester.
|
|
132
|
-
*/
|
|
133
|
-
icon?: string;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* serialized config of the tester.
|
|
137
|
-
*/
|
|
138
|
-
displayConfig?(): string;
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* path to the config in the filesystem.
|
|
142
|
-
*/
|
|
143
|
-
configPath?: string;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* id of the tester.
|
|
147
|
-
*/
|
|
148
|
-
id: string;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* on test run complete. (applies only during watch)
|
|
152
|
-
* @param callback
|
|
153
|
-
*/
|
|
154
|
-
onTestRunComplete?(callback: CallbackFn): Promise<void>;
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* execute tests on all components in the given execution context.
|
|
158
|
-
*/
|
|
159
|
-
test(context: TesterContext): Promise<Tests>;
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* watch tests on all components
|
|
163
|
-
*/
|
|
164
|
-
watch?(context: TesterContext): Promise<Tests>;
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* return the tester version.
|
|
168
|
-
*/
|
|
169
|
-
version(): string;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export type CallbackFn = (testSuite: Tests) => void;
|