I think Karma is running tests twice - phantomjs

I am trying to test my react app using karma(on PhantomJS2), jasmine, and enzyme. I have noticed that some of my tests are failing. It seems to be because each it block is being called twice(I have confirmed this by putting prints in all my it blocks and they all print twice). I have looked up solutions, but none seem to work. I will include as much details as possible.
Here is my karma.config file:
files: [
{ pattern: 'test/**/*.tsx' }
],
I have also tried these but this had the same result as above:
files: [
{ pattern: 'test/**/*.tsx', watched: false, served: true, included: true }
],
I also tried this, but this caused karma to break:
files: [
{ pattern: 'test/**/*.tsx', included: false }
],
Here an example of one of my it blocks that runs twice(Although this passes if ran, the length being off is causing other tests to fail which I commented out):
it('renders the items', () => {
const wrapper = mount(<PortfolioList />);
const length = 5;
const items = fillArray(length);
// tslint:disable-next-line:no-console
console.log('LENGTH: ' + items.length); //OUTPUTS(twice): LENGTH: 10
wrapper.setState({results: items});
expect(wrapper.find('tbody').children().length).toBe(length);
});
Here is fillArray, even thought it is being called twice. Shouldn't it return an array of size 5 twice in my example? Why is it 10?:
function fillArray(length: number) {
const ary = new Array(length);
for (let i = 0; i < length; i++) {
ary.push(item);
}
return ary;
}
Here is a pic of what I feel is relevant(This is pasted together):

Related

Can rollup-plugins access the AST created by previous plugins in the plugin chain?

We use multiple rollup-plugins that parse their input to an AST. As they run on the same files, each file is parsed multiple times. Can this be optimized, so that each file is parsed only once? Minimal example:
// rollup.config.js
import {createFilter} from '#rollup/pluginutils';
import {simple} from 'acorn-walk';
import {attachComments} from 'astravel';
import {generate} from 'astring';
export default {
input: 'src/main.js',
output: {file: 'bundle.js', format: 'cjs'},
plugins: [{
name: 'plugin1',
transform(code, id) {
const comments = [];
const ast = this.parse(code, {onComment: comments});
attachComments(ast, comments);
simple(ast, {
Identifier(n) {
// rewrite wrong to right
if (n.name === 'wrong') n.name = 'right';
}
});
return {
code: generate(ast, {comments: true}),
ast,
map: null /* minimal example, won't create a source map here */
};
}
}, {
name: 'plugin2',
transform(code, id) {
const comments = [];
const ast = this.parse(code, {onComment: comments});
attachComments(ast, comments);
simple(ast, {
CallExpression(n) {
// rewrite mylog(...) to console.log(...)
if (n.callee.type === 'Identifier' && n.callee.name === 'mylog') {
n.callee = {
type: 'MemberExpression',
object: {type: 'Identifier', name: 'console', start: n.start, end: n.end},
property: {type: 'Identifier', name: 'log', start: n.start, end: n.end},
computed: false,
start: n.start,
end: n.end
}
}
}
});
return {
code: generate(ast, {comments: true}),
ast,
map: null /* minimal example, won't create a source map here */
};
}
}]
};
Now I understand that transform() can return an AST, so that parsing doesn't have to happen twice. And I understand that this.parse() uses the rollup-internal acorn instance. My simple mind thought that this.parse() could return the AST created by previous transform() calls, if available. But I assume that all sorts of demons await on that road, e.g. when this.parse() was called with different options.
Is there a different way achieve what I described? A different hook maybe?
I would love to not have all plugins in one and switching them on and off via options (I see that this would be a solution, but a really cumbersome one).

Commitlint - Allow '/' in scope-enum

In my Angular project, I want to extend #commitlint/config-conventional with some pre-defined scopes.
The Angular project has a library for UI components (generated via ng generate library) and a default app which consumes the UI library.
In commitlint.config.js I've added the following lines:
module.exports = {
extends: ['#commitlint/config-conventional'],
rules: {
'scope-enum': [
2,
'always',
[
'ui-components',
'ui-components/badge',
'ui-components/button',
'ui-components/tooltip',
'core',
'account',
'plugins',
'settings',
'projects',
'shared',
'styles'
]
]
}
};
However, when I try to commit something with the scope: 'ui-components/tooltip':
fix(ui-components/tooltip): fix border
I get a commitlint error, saying that:
⧗ input: fix(ui-components/tooltip): fix border
✖ scope must be one of [ui-components, ui-components/badge, ui/button, ui-components/tooltip, core, account, plugins, settings, projects, shared, styles] [scope-enum]
✖ found 1 problems, 0 warnings
Unfortunately slashes aren't allowed in scopes.
To get around this I replace / with two dashes (--).
I wrote a script to grab subfolders and return an array:
https://gist.github.com/trevor-coleman/51f1730044e14081faaff098618aba36
[
'ui-components',
'ui-components--badge',
'ui-components--button',
'ui-components--tooltip',
...
]
According to source code, Commitlint use / for multiple scopes.
It means, you can commit like fix(core/account): fix border but you can't commit fix(ui-components/tooltip): fix border because you need to add tooltip in to your scopes first.
Here is source code: https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/rules/src/scope-enum.ts
Also, it is mentioned in here: https://github.com/conventional-changelog/commitlint/blob/master/docs/concepts-commit-conventions.md#multiple-scopes
You can write your own custom plugin to check scopes, I had the same issue, so I wrote one to solve this problem, see example commitlint.config.js below:
module.exports = {
extends: ["#commitlint/config-conventional"],
rules: {
"enhanced-scope-enum": [
2,
"always",
[
"ui-components",
"ui-components/badge",
"ui-components/button",
"ui-components/tooltip",
"core",
"account",
"plugins",
"settings",
"projects",
"shared",
"styles",
],
],
},
plugins: [
{
rules: {
"enhanced-scope-enum": (parsed, when = "always", value = []) => {
if (!parsed.scope) {
return [true, ""];
}
// only use comma sign as seperator
const scopeSegments = parsed.scope.split(",");
const check = (value, enums) => {
if (value === undefined) {
return false;
}
if (!Array.isArray(enums)) {
return false;
}
return enums.indexOf(value) > -1;
};
const negated = when === "never";
const result =
value.length === 0 ||
scopeSegments.every((scope) => check(scope, value));
return [
negated ? !result : result,
`scope must ${negated ? `not` : null} be one of [${value.join(
", "
)}]`,
];
},
},
},
],
}

In Vue (webpack), how can I make certain functions run at compile-time?

For example, in a component I have a really long constant string. When the component loads, the string goes through a function that edits it in a certain way. So it's always the same. I could've as well written the already-edited version but that would be inconvenient for my project.
So it makes me wonder - is there any way to make that function run at compile-time so when webpack makes a bundle, it will have the edited version only and won't have to run that function every time the component loads, which will be good for performance.
In general, there is a lot of things in my project that I would like to precalculate at compile-time. Say, I write a component with a constant prop:
<Number n="123">
Inside that component, I turn 123 into a string one hundred and twenty three, so in the end it'll turn into:
<div> one hundred and twenty three </div>
Let's imagine that that translation function is actually heavy to calculate. In that case it'd be great if those calculations were made at compile time.
Thanks in advance!
I was wondering about the same thing and realized that there are better solutions to my problem. It all about how you architect things. As I know there is no simple way to do so in Vue.
Here is a list of ideas you might consider appealing:
Despite the fact that the component is mounting multiple times you can make some code inside of it execute once during page load.
// BEFORE
export default {
methods: {
test() {}
},
mounted() {
const calculated = this.test(); // executes on every component mount
console.log(calculated);
}
}
// AFTER
// executed only once during the page load
const calculated = (() => { // code of test function is here });
export default {
mounted() {
console.log(calculated);
}
}
Optimize heavy function. There tons of opportunities to do so. Learn about complexity of algorithms, O(n) and methods of optimization. Moreover, there might be a npm library already optimized for your task.
// Let's assume you need to return list of users that are admins (based on some sort of groups)
// ---
// You have:
// users = [{id: 1, groupId: 1}, {id: 2, groupId: 1}, {id: 3, groupId: 2}]
// groups = [{ id: 1, isAdmin: true }, { id: 2, isAdmin: false }]
// --
// Expected: {id: 1, groupId: 1}, {id: 2, groupId: 1}
// Quick solution: O(N x M)
// If we have 1000 users and 1000 groups it would need 1000x1000 operations which is 1000000
const getAdminUsers = (users, groups) => {
return users.filter(user =>
groups.some(group => user.groupId === group.id && group.isAdmin)
);
};
// Faster solution: O(N + M)
// If we have 1000 users and 1000 groups it would need 1000+1000 operations which is just 2000
const getAdminUsers = (users, groups) => {
const groupIdToAdminMap = {};
for(let i = 0; i < groups.length; i++) {
groupIdToAdminMap[i] = groups[i].isAdmin;
}
return users.filter(user => groups[user.groupId]);
}
Cache. I know, cache is considered to be one of the hardest things in computer science but something as simple as memoization can help a lot. The idea is prevent computations for values which has been computed before
// let's take existing implementation of memoization just to save time
import memoize from 'lodash/memoize'
export default {
methods: {
test() {}
},
beforeCreate() {
this.test = memoize(this.test)
}
mounted() {
const calculated1 = this.test('123'); // triggers expensive calculations
const calculated2 = this.test('123'); // doesn't trigger expensive calculations
console.log(calculated1, calculated2);
}
}
Supposing none of the options about worked for you, try babel-plugin-preval. It allows to execute some code at transpile time.
// BEFORE transpiling
const greeting = preval`
const fs = require('fs')
module.exports = fs.readFileSync(require.resolve('./greeting.txt'), 'utf8')
`;
// AFTER transpiling
const greeting = 'Hello world!'

Vue.js: Is it possible to do "Conditional compilation" with a Vue project?

In for example Swift/iOS development, it's possible to differentiate builds for different environments with "flags" such as:
#if STAGING
// one set of logic here
#endif
#if PRODUCTION
// another set of logic here
#endif
Is it possible to achieve the same with a Vue.js project, and how would we go about doing it? I am aware of makes different routes conditionally available for different roles (which is also quite neat), but I am optimally looking for the option to differentiate on a source code level.
Hope someone has some great insights! It could include:
How to exclude parts of a file (such as the #if STAGING above) from a build target
How to exclude entire files from a build target
etc.
you have the ability to use this syntax
if(process.env.NODE_ENV === 'production') {
console.log("this is the prod env!!!!!!!!!!");
config.output.path = path.resolve(__dirname, "dist");
}
make sure that when you run the script with the correct env's for each environment (local, dev, staging, prod etc ..) :D
just change the vue-loader output.
the source code
<template v-if="process.env.NODE_ENV === 'development'">
development only
</template>
default output
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"div",
{ attrs: { id: "app" } },
[
_vm.process.env.NODE_ENV === "development"
? [_vm._v(" development only ")]
: _vm._e(),
_c("router-view")
],
2
)
}
just use regex to replace _vm.process.env. by process.env is ok.
// webpack.config.js
module: {
rules: [{
// must set post
enforce: 'post',
test: /\.vue$/,
use: [{
loader: './myLoader'
}]
}]
}
// myLoader.js
module.exports = function (source, map) {
if (source.indexOf('_vm.process.env') > -1) {
source = source.replace(/_vm.process.env/g, 'process.env')
}
this.callback(
null,
source,
map
)
}
Final the vue-loader result change
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"div",
{ attrs: { id: "app" } },
[
// change to true
true
? [_vm._v(" development only ")]
: undefined,
_c("router-view")
],
2
)
}

TypeScript Compiler API: Unexpected behavior of getReturnTypeOfSignature() when return type is an array

Suppose the following component class with a public method get_ids() that returns an array of literal values (here: number[]):
class Component {
private _ids: number[];
public get_ids() {
return this._ids;
}
}
The following code works fine as long as the method returns anything else than an array:
const componentSymbol: ts.Symbol = checker.getSymbolAtLocation( componentNode.name ) // componentNode is the AST node of the component class
const componentType: ts.Type = checker.getDeclaredTypeOfSymbol( componentSymbol );
const property: ts.Symbol = componentType.getProperties[1]; // 2nd property represents the method 'get_ids()'
const methodDeclaration = property.getDeclarations()[0] as ts.MethodDeclaration;
const signature = checker.getSignatureFromDeclaration( methodDeclaration );
const returnType = checker.getReturnTypeOfSignature( signature );
However, since get_ids() returns an array, returnType equals:
{
flags: 1,
id: 4,
intrinsicName: "unknown"
}
And there is basically no information that this should be an array of numbers.
Also, the return value of checker.typeToString(checker.getTypeAtLocation(methodDeclaration)) is () => any. I would expect it to be () => number[].
What am I doing wrong here?
I have experimented a bit with the compiler options I have read in from the tsconfig.json file. Turns out that the lib option causes the problem. When commented out, I'm getting the expected result (() => number).
const compilerOptions = {
allowJs: false,
checkJs: false,
diagnostics: false,
inlineSourceMap: true,
inlineSources: true,
jsx: ts.JsxEmit.React,
// lib: [
// // "es5",
// // "dom",
// // "scripthost",
// // "es2015.iterable"
// ],
module: ts.ModuleKind.AMD,
noEmitHelpers: true,
strictNullChecks: false,
tripInternal: true,
target: ts.ScriptTarget.ES5,
downlevelIteration: true,
baseUrl: "./",
pretty: true,
experimentalDecorators: true
};