Unit testing vue components with Jest issue - javascript

I'm struggling trying to unit test some components in an app and I would really apreciate if someone could help me.
I've already set up the Jest enviroment for testing in Vue and things work just fine until I try to test a component that rely on other modules with functions in it. These functions are helpers useful for importing vuex actions and getters quicker in the components. Here there are some lines of code that I hope could help with the problem:
this is the error that Jest throws in the console:
/app # yarn test:unit /common / page - header / ProjectWizard.spec.js
yarn run v1 .22 .5
$ jest / common / page - header / ProjectWizard.spec.js
FAIL src / specs / common / page - header / projectWizard.spec.js
● Test suite failed to run
Configuration error:
Could not locate module # / state / helpers mapped as:
/app/$1.
Please check your configuration
for these entries: {
"moduleNameMapper": {
"/^#\/(.*)$/": "/app/$1"
},
"resolver": undefined
}
57 |
import VueGoogleAutocomplete from '../../../node_modules/vue-google-autocomplete'
58 |
> 59 |
import {
|
^
60 | contractTypesMethods,
61 | contractTypesGetters,
62 | installationTypesMethods,
at createNoMappedModuleFoundError(node_modules / jest - resolve / build / index.js: 551: 17)
at src / components / builder / ProjectWizard.vue: 59: 1
at Object. < anonymous > (src / components / builder / ProjectWizard.vue: 491: 3)
this is the ProjectWizard file:
<template>
<form-wizard
#on-complete="onComplete(model)"
error-color="#f1b44c"
color="#EC641C"
nextButtonText="Avanti"
backButtonText="Indietro"
:finishButtonText="(isModifying) ? 'Conferma modifiche' : 'Crea Progetto'"
id="project-form-wizard"
style="overflow: hidden;"
>
<tab-content title="Informazioni preliminari" icon="mdi mdi-folder-information" :before-change="validateFirstTab">
<vue-form-generator-component :model="model" :schema="zeroTabSchema" :options="formOptions" ref="zeroTabSchema">
</vue-form-generator-component>
<b-form-group featured class="col-12" label="Indirizzo (Via etc.)" label-for="indirizzo-progetto">
<vue-google-autocomplete required keypress types="" ref="address" id="indirizzo-progetto"
classname="form-control" placeholder="Inserisci qui l'indirizzo del progetto"
v-on:placechanged="getAddressData" country="it">
</vue-google-autocomplete>
</b-form-group>
<vue-form-generator-component :model="model" :schema="firstTabSchema" :options="formOptions" ref="firstTabForm">
</vue-form-generator-component>
</tab-content>
<tab-content
title="Dettagli progetto"
icon="mdi mdi-home-search-outline"
:before-change="validateSecondTab"
>
<vue-form-generator-component
:model="model"
:schema="secondTabSchema"
:options="formOptions"
ref="secondTabForm"
></vue-form-generator-component>
</tab-content>
</form-wizard>
</template>
<script>
import {
FormWizard,
TabContent
} from "vue-form-wizard";
import VueFormGenerator from 'vue-form-generator'
import {
resources
} from "../../../src/utils/validation"
Object.assign(VueFormGenerator.validators.resources, resources)
import VueGoogleAutocomplete from '../../../node_modules/vue-google-autocomplete'
import {
contractTypesMethods,
contractTypesGetters,
installationTypesMethods,
installationTypesGetters,
interventionTypesMethods,
interventionTypesGetters,
contactsMethods,
projectManagementMethods,
projectManagementGetters,
projectsMethods
} from "#/state/helpers";
// STATELESS
import {
fetchContacts
} from '#/stateless/modules/common'
export default {
data() {
var vm = this;
return {
...contractTypesGetters,
...installationTypesGetters,
...interventionTypesGetters,
...projectManagementGetters,
contractTypes: [],
selectedContract: "",
installationTypesFiltered: [],
interventionTypesFiltered: [],
installationTypes: [],
interventionTypes: [],
contactPersons: [],
address: '',
model: {
projectName: '',
address: {
partialStreet: '',
streetNumber: '',
city: '',
province: '',
country: '',
CAP: '',
state: ''
},
contractType: '',
installationType: '',
interventionType: '',
contactPerson: '',
description: ''
},
formOptions: {
validationErrorClass: "has-error",
validationSuccessClass: "has-success",
validateAfterChanged: true,
},
zeroTabSchema: {
fields: [{
type: "input",
inputType: "text",
label: "Nome del progetto",
model: "projectName",
required: true,
featured: true,
maxlength: 64,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-12'
},]
},
firstTabSchema: {
fields: [
{
type: "input",
inputType: "text",
label: "Via",
model: "address.partialStreet",
required: true,
featured: true,
maxlength: 128,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-6'
},
{
type: "input",
inputType: "text",
label: "Numero civico",
model: "address.streetNumber",
required: true,
featured: true,
maxlength: 16,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-6'
},
{
type: "input",
inputType: "text",
label: "Città",
model: "address.city",
required: true,
featured: true,
maxlength: 64,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-6'
},
{
type: "input",
inputType: "text",
label: "Provincia",
model: "address.province",
required: true,
featured: true,
maxlength: 2,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-6'
},
{
type: "input",
inputType: "text",
label: "Paese",
model: "address.country",
required: true,
featured: true,
maxlength: 32,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-6'
},
{
type: "input",
inputType: "text",
label: "CAP",
model: "address.CAP",
required: true,
featured: true,
min: 5,
maxlength: 8,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-6'
},
{
type: "input",
inputType: "text",
visible: false,
label: "Regione",
model: "address.state",
required: true,
featured: true,
maxlength: 32,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-6'
},
]
},
secondTabSchema: {
fields: [{
type: "select",
label: "Tipo di appalto",
model: "contractType",
required: true,
featured: true,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-12',
selectOptions: {
hideNoneSelectedText: true
},
onChanged(event) {
const filterdTypes = vm.installationTypes.filter((element) => {
return element.contractType === event.contractType
})
let installation = document.getElementById('insediamento')
installation.innerHTML = ''
installation.disabled = false
for (let i = 0; i < filterdTypes.length; i++) {
installation.options[i] = new Option(
filterdTypes[i].name,
filterdTypes[i].id
)
}
installation.value = ""
let intervention = document.getElementById('tipologia-di-intervento')
intervention.value = ""
},
values: () => {
return this.contractTypes
},
},
{
type: "select",
disabled: "true",
label: "Insediamento",
model: "installationType",
required: true,
featured: true,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-12',
selectOptions: {
hideNoneSelectedText: true
},
onChanged(event) {
const filterdTypes = vm.interventionTypes.filter((element) => {
return element.installationType === event.installationType
})
let intervention = document.getElementById('tipologia-di-intervento')
intervention.innerHTML = ''
intervention.disabled = false
for (let i = 0; i < filterdTypes.length; i++) {
intervention.options[i] = new Option(
filterdTypes[i].name,
filterdTypes[i].id
)
}
intervention.value = ""
},
values: () => {
return this.isModifying ? this.installationTypes : []
},
},
{
type: "select",
disabled: "true",
noneSelectedText: "",
label: "Tipologia di intervento",
model: "interventionType",
required: true,
featured: true,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-12',
selectOptions: {
hideNoneSelectedText: true
},
values: () => {
return this.isModifying ? this.interventionTypes : []
},
},
{
type: "select",
label: "Referente di progetto",
model: "contactPerson",
required: true,
featured: true,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-12',
selectOptions: {
hideNoneSelectedText: true
},
hint: '<div id="new-contact-creation-project"><i class="mdi mdi-account-multiple-plus"></i> <span>Crea nuovo referente</span></div>',
values: () => {
return this.contactPersons
},
},
{
type: "textArea",
label: "Descrizione",
model: "description",
featured: true,
maxlength: 512,
validator: VueFormGenerator.validators.string,
styleClasses: 'col-12'
}
]
}
}
},
components: {
FormWizard,
TabContent,
'vue-form-generator-component': VueFormGenerator.component,
VueGoogleAutocomplete
},
computed: {
isModifying() {
return (this.$router.currentRoute.name == 'builderHome') ? false : true
},
},
methods: {
...contractTypesMethods,
...installationTypesMethods,
...interventionTypesMethods,
...contactsMethods,
...projectsMethods,
...projectManagementMethods,
onComplete: function (json) {
if (this.isModifying) {
this.updateProject({
projectId: this.currentProject().id,
name: this.model.projectName,
description: this.model.description,
address: this.model.address.partialStreet,
streetNumber: this.model.address.streetNumber,
city: this.model.address.city,
province: this.model.address.province,
country: this.model.address.country,
zipCode: this.model.address.CAP,
contactPerson: this.model.contactPerson,
contractType: this.model.contractType,
installationType: this.model.installationType,
interventionType: this.model.interventionType
}).then(() => location.reload())
} else {
this.addProject(json)
.then(response => {
if (response) {
this.$router.push({
name: 'BOMUpload',
params: { projectId: response.data.data.createProject.project.id }
})
}
})
}
},
validateFirstTab: function () {
if (!this.model.projectName) {
return alert('Il nome del progetto è obbligatorio')
}
return this.$refs.firstTabForm.validate();
},
validateSecondTab: function () {
return this.$refs.secondTabForm.validate();
},
newContactPersonCreation() {
this.$emit('new-contact-person-creation')
},
getAddressData: function (addressData, placeResultData) {
this.address = addressData;
this.model.address.country = addressData.country;
this.model.address.city = addressData.locality;
if (placeResultData.address_components[4].short_name == 'IT') {
this.model.address.province = placeResultData.address_components[2].short_name
} else if (placeResultData.address_components[4].short_name.length > 2) {
this.model.address.province = placeResultData.address_components[3].short_name
} else {
this.model.address.province = placeResultData.address_components[4].short_name
}
this.model.address.partialStreet = addressData.route;
this.model.address.streetNumber = addressData.street_number;
this.model.address.CAP = addressData.postal_code;
this.model.address.state = addressData.administrative_area_level_1;
},
getContacts() {
fetchContacts()
.then(response => {
this.contactPersons = response.map(contact => {
return { id: contact.node.id, name: contact.node.name }
})
this.model.contactPerson = this.contactPersons.slice(-2).pop().id
if (this.isModifying) {
this.model.contactPerson = this.currentProject().contactPerson.id
}
})
.catch(err => console.error('Riga 417 ProjectWizard.vue', err))
}
},
mounted() {
if (this.isModifying) {
this.model.projectName = this.currentProject().name
this.model.address.partialStreet = this.currentProject().address.address
this.model.address.streetNumber = this.currentProject().address.streetNumber
this.model.address.city = this.currentProject().address.city
this.model.address.province = this.currentProject().address.province
this.model.address.country = this.currentProject().address.country
this.model.address.CAP = this.currentProject().address.zipCode
this.model.description = this.currentProject().description
}
var newContact = document.querySelector("#new-contact-creation-project")
newContact.onclick = () => {
this.newContactPersonCreation()
};
// CONTRACT TYPES
this.fetchContractTypes()
.then(response => {
if (response) {
let processedContractTypes = []
this.contractTypes = this.allContractTypes()
this.contractTypes.forEach(contractType => {
let contractTypeObj = { id: contractType.node.id, name: contractType.node.name }
processedContractTypes.push(contractTypeObj)
})
this.contractTypes = processedContractTypes
if (this.isModifying){
this.model.contractType = this.currentProject().contractType.id
}
} else {
alert('Not working fetchContractTypes')
}
})
// INSTALLATION TYPES
this.fetchInstallationTypes()
.then(response => {
if (response) {
let processedInstallationTypes = []
this.installationTypes = this.allInstallationTypes()
this.installationTypes.forEach(installationType => {
let installationTypeObj = {
id: installationType.node.id,
name: installationType.node.name,
contractType: installationType.node.contractType.id
}
processedInstallationTypes.push(installationTypeObj)
})
this.installationTypes = processedInstallationTypes
if (this.isModifying){
this.model.installationType = this.currentProject().installationType.id
}
} else {
alert('Not working fetchInstallationTypes')
}
})
// INTERVENTION TYPES
this.fetchInterventionTypes()
.then(response => {
if (response) {
let processedInterventionTypes = []
this.interventionTypes = this.allInterventionTypes()
this.interventionTypes.forEach(interventionType => {
let interventionTypeObj = {
id: interventionType.node.id,
name: interventionType.node.name,
installationType: interventionType.node.installationType.id,
}
processedInterventionTypes.push(interventionTypeObj)
})
this.interventionTypes = processedInterventionTypes
if (this.isModifying){
this.model.interventionType = this.currentProject().interventionType.id
}
} else {
alert('Not working fetchInterventionTypes')
}
})
// CONTACT PERSONS
this.getContacts()
},
}
</script>
this is my jest configuration file:
module.exports = {
verbose: true,
roots: ["<rootDir>/src/", "<rootDir>/src/specs/"],
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/$1',
"~(.*)$": "<rootDir>/$1"
},
transform: {
"^.+\\.js$": "babel-jest",
'.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2|svg)$': 'jest-transform-stub',
"^.+\\.vue$": "vue-jest"
},
snapshotSerializers: [
"<rootDir>/node_modules/jest-serializer-vue"
],
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!vue-google-autocomplete)',
'<rootDir>/(?!)'
]
}
and finally this is the code in the spec file:
import { mount } from '#vue/test-utils'
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import Component from '#/src/components/builder/ProjectWizard'
Vue.use(BootstrapVue)
describe('ProjectWizard', () => {
it('should render a card for each project received from its parent', () => {
const wrapper = mount(Component)
console.log(wrapper)
})
})
I dont't understand why Jest isn't able to find those functions since the module's location is right.

Related

Combine duplicate tokens inside huge JSON file into nested array of objects using React

I looked at several of the suggested solutions but none seemed to rise to this confounding data formatting challenge.
I have a huge JSON file (over 100k rows) and massive duplicates of data all as top level objects. Here's an example:
[
{
"manufacturer":"Samsung",
"device":"Galaxy A32 5G",
"model":"SM-A326B",
"chipset":"Mediatek MT6853V/NZA",
"date":"2022-01-01",
"fw_id":"A326BXXS4AVA1",
"android":"R(Android 11)",
"known_passcode":false,
"afu":false,
"bfu":false,
"bruteforce":false
},
{
"manufacturer":"Samsung",
"device":"Galaxy A32 5G",
"model":"SM-A326U",
"chipset":"Mediatek MT6853V/NZA",
"date":"2021-03-01",
"fw_id":"A326USQU1AUD4",
"android":"R(Android 11)",
"known_passcode":true,
"afu":false,
"bfu":true,
"bruteforce":true
},
{
"manufacturer":"Samsung",
"device":"Galaxy A32 5G",
"model":"SM-A326U1",
"chipset":"Mediatek MT6853V/NZA",
"date":"2021-09-01",
"fw_id":"A326U1UEU5AUJ2",
"android":"R(Android 11)",
"known_passcode":true,
"afu":false,
"bfu":true,
"bruteforce":true
},
{
"manufacturer":"LGE",
"device":"LG K31",
"model":"LGL355DL",
"chipset":"Mediatek MT6762",
"date":"unknown",
"fw_id":"L355DL10l",
"android":"unknown",
"known_passcode":false,
"afu":false,
"bfu":false,
"bruteforce":false
}
]
This needs to be organized so that data points like manufacturer, device, model are not duplicated hundreds of times.
Btw, here's a JSFiddle to play with:
https://jsfiddle.net/xpancom/Lq7duahv/
Ideally, the JSON format would be the following:
[
{
"manufacturers": [
{
"manufacturer": "Samsung",
"devices": [
{
"device": "Galaxy A32 5G",
"models": [
{
"model": "SM-A326B",
"data": [
{
"chipset": "Mediatek MT6853V/NZA",
"date": "2022-01-01",
"fw_id": "A326BXXS4AVA1",
"android": "R(Android 11)",
"known_passcode": false,
"afu": false,
"bfu": false,
"bruteforce": false
},
{
"chipset": "Mediatek MT6853V/NZA",
"date": "2021-09-01",
"fw_id": "A326BXXU3AUH7",
"android": "R(Android 11)",
"known_passcode": true,
"afu": false,
"bfu": true,
"bruteforce": true
}
]
},
{
"model": "SM-A326U1",
"data": [
{
"chipset": "Mediatek MT6853V/NZA",
"date": "2021-09-01",
"fw_id": "A326U1UEU5AUJ2",
"android": "R(Android 11)",
"known_passcode": true,
"afu": false,
"bfu": true,
"bruteforce": true
}
]
}
]
}
]
},
{
"manufacturer": "LGE",
"devices": [
{
"device": "LG K31",
"models": [
{
"model": "SM-A326B",
"data": [
{
"chipset": "Mediatek MT6762",
"date": "unknown",
"fw_id": "L355DL10l",
"android": "unknown",
"known_passcode": false,
"afu": false,
"bfu": false,
"bruteforce": false
}
]
}
]
}
]
}
]
}
]
Working in React, here's what I've got so far in trying to massage this data:
const source = data;
const destination = [];
const classifiedTokens = []; // will be used to stored already classified tokens
const classifiedTokensModel = []; // will be used to stored already classified tokens for models
const getNextTokenArray = (source) => {
let unusedToken = null;
const nextTokenArray = source.filter(function (element) {
if (!unusedToken && !classifiedTokens.includes(element['device'])) {
unusedToken = element['device'];
classifiedTokens.push(unusedToken);
}
return unusedToken ? unusedToken === element['device'] : false;
});
return unusedToken ? nextTokenArray : null;
};
// Pass in arrays deconstructed from addToDestination to process third tier nested objects for models
const getNextTokenArrayModel = (tokenObject) => {
let tokenObjectDevice = tokenObject['device'];
let tokenObjectData = tokenObject['data'];
let unusedTokenModel = null;
const nextTokenArrayModel = tokenObjectData.filter(function (element) {
if (!unusedTokenModel && !classifiedTokensModel.includes(element['model'])) {
unusedTokenModel = element['model'];
classifiedTokensModel.push(unusedTokenModel);
}
return unusedTokenModel ? unusedTokenModel === element['model'] : false;
});
//return unusedTokenModel ? nextTokenArrayModel : null;
if (unusedTokenModel) {
if (nextTokenArrayModel.length === 0) return;
let res = {
device: tokenObjectDevice,
model: nextTokenArrayModel[0]['model'],
data: [],
};
nextTokenArrayModel.forEach((element) => {
res.data.push({
manufacturer: element.manufacturer,
chipset: element.chipset,
date: element.date,
fw_id: element.fw_id,
android: element.android,
knownPasscode: element.knownPasscode,
afu: element.afu,
bfu: element.bfu,
bruteforce: element.bruteforce,
});
});
destination.push(res);
} else {
return null;
}
};
const addToDestination = (tokenArray) => {
if (tokenArray.length === 0) return;
let res = {
device: tokenArray[0]['device'],
data: [],
};
tokenArray.forEach((element) => {
res.data.push({
manufacturer: element.manufacturer,
model: element.model,
chipset: element.chipset,
date: element.date,
fw_id: element.fw_id,
android: element.android,
knownPasscode: element.knownPasscode,
afu: element.afu,
bfu: element.bfu,
bruteforce: element.bruteforce,
});
});
getNextTokenArrayModel(res); // Call this to process and group nested model duplicates by device
//destination.push(res);
};
let nextTokenArray = getNextTokenArray(source);
while (nextTokenArray) {
addToDestination(nextTokenArray);
nextTokenArray = getNextTokenArray(source);
}
setTimeout(() => {
document.getElementById('root').innerHTML =
'<pre>' + JSON.stringify(destination, null, 2) + '</pre>';
}, 1000);
};
And here's the JSFiddle again:
https://jsfiddle.net/xpancom/Lq7duahv/
Who can smash this data formatting dilemma?
This answer is not React specific, but one approach would be to use array.reduce() to transform each level/node of the structure as shown in the code snippet below.
const source = [
{
manufacturer: 'Samsung',
device: 'Galaxy A32 5G',
model: 'SM-A326B',
chipset: 'Mediatek MT6853V/NZA',
date: '2022-01-01',
fw_id: 'A326BXXS4AVA1',
android: 'R(Android 11)',
known_passcode: false,
afu: false,
bfu: false,
bruteforce: false,
},
{
manufacturer: 'Samsung',
device: 'Galaxy A32 5G',
model: 'SM-A326B',
chipset: 'Mediatek MT6853V/NZA',
date: '2022-01-01',
fw_id: 'A326BXXS4AVA1',
android: 'R(Android 11)',
known_passcode: false,
afu: false,
bfu: false,
bruteforce: false,
},
{
manufacturer: 'Samsung',
device: 'Galaxy A32 5G',
model: 'SM-A326U',
chipset: 'Mediatek MT6853V/NZA',
date: '2021-03-01',
fw_id: 'A326USQU1AUD4',
android: 'R(Android 11)',
known_passcode: true,
afu: false,
bfu: true,
bruteforce: true,
},
{
manufacturer: 'Samsung',
device: 'Galaxy A32 5G',
model: 'SM-A326U1',
chipset: 'Mediatek MT6853V/NZA',
date: '2021-09-01',
fw_id: 'A326U1UEU5AUJ2',
android: 'R(Android 11)',
known_passcode: true,
afu: false,
bfu: true,
bruteforce: true,
},
{
manufacturer: 'LGE',
device: 'LG K31',
model: 'LGL355DL',
chipset: 'Mediatek MT6762',
date: 'unknown',
fw_id: 'L355DL10l',
android: 'unknown',
known_passcode: false,
afu: false,
bfu: false,
bruteforce: false,
},
];
function generateTree(data, key) {
return data.reduce((acc, val) => {
// Split the key name from the child data
const { [key.name]: keyName, ...childData } = val;
// Find a tree item in the structure being generated
const treeItem = acc.find((item) => item[key.name] === keyName);
if (treeItem) {
// If found, append child data
treeItem[key.child].push(childData);
} else {
// If not found, create new key and append child data
acc.push({ [key.name]: keyName, [key.child]: [childData] });
}
return acc;
}, []);
}
// Generate manufacturer/device structure
const manufacturers = generateTree(source, {
name: 'manufacturer', // Key name to use as grouping identifier
child: 'devices', // Key name for child data
});
// Generate device/model structure
manufacturers.forEach((manufacturer) => {
manufacturer.devices = generateTree(manufacturer.devices, {
name: 'device',
child: 'models',
});
// Generate model/data structure
manufacturer.devices.forEach((device) => {
device.models = generateTree(device.models, {
name: 'model',
child: 'data',
});
});
});
const destination = [{ manufacturers }];
console.log(destination);

Show data to Ag Grid Vue

I am trying to load data into the VueJS table using the ag Grid vue plugin that I have in a template, I have tried several ways but I am not getting the result I am looking for ..., getting the following result:
{
"users": [
{
"id": 1,
"client_id": 1,
"cop_id": null,
"role_id": null,
"name": "Bart",
"email": "correo.123#gmail.com",
"created_at": null,
"updated_at": null
}
]
}
then I have my Vue view in this way, and sorry for including all the code, but since the template is working with everything that comes, I prefer to deliver all the detail, I have tried to make changes to the created method with axios, but I did not It works, unfortunately I can't load the data into the table, I hope you can guide me to fix my loading error.
<template>
<div id="page-user-list">
<!-- AgGrid Table -->
<ag-grid-vue
ref="agGridTable"
:components="components"
:gridOptions="gridOptions"
class="ag-theme-material w-100 my-4 ag-grid-table"
:columnDefs="columnDefs"
:defaultColDef="defaultColDef"
:rowData="rowData"
rowSelection="multiple"
colResizeDefault="shift"
:animateRows="true"
:floatingFilter="true"
:pagination="true"
:paginationPageSize="paginationPageSize"
:suppressPaginationPanel="true"
:enableRtl="$vs.rtl">
</ag-grid-vue>
<vs-pagination
:total="totalPages"
:max="10"
v-model="currentPage" />
</div>
</template>
<script>
import { AgGridVue } from "ag-grid-vue"
import '#sass/vuexy/extraComponents/agGridStyleOverride.scss'
import vSelect from 'vue-select'
import axios from 'axios'
// Store Module
//import moduleUserManagement from '#/store/user-management/moduleUserManagement.js'
// Cell Renderer
import CellRendererLink from "./cell-renderer/CellRendererLink.vue"
import CellRendererStatus from "./cell-renderer/CellRendererStatus.vue"
import CellRendererVerified from "./cell-renderer/CellRendererVerified.vue"
import CellRendererActions from "./cell-renderer/CellRendererActions.vue"
export default {
components: {
AgGridVue,
vSelect,
// Cell Renderer
CellRendererLink,
CellRendererStatus,
CellRendererVerified,
CellRendererActions,
},
data() {
return {
// Filter Options
roleFilter: { label: 'Todos', value: 'all' },
roleOptions: [
{ label: 'Todos', value: 'all' },
{ label: 'Administador', value: 'admin' },
{ label: 'Usuario', value: 'user' },
{ label: 'Staff', value: 'staff' },
],
statusFilter: { label: 'Todos', value: 'all' },
statusOptions: [
{ label: 'Todos', value: 'all' },
{ label: 'Activo', value: 'active' },
{ label: 'Desactivado', value: 'deactivated' },
{ label: 'Bloqueado', value: 'blocked' },
],
isVerifiedFilter: { label: 'Todos', value: 'all' },
isVerifiedOptions: [
{ label: 'Todos', value: 'all' },
{ label: 'Si', value: 'yes' },
{ label: 'No', value: 'no' },
],
departmentFilter: { label: 'Todos', value: 'all' },
departmentOptions: [
{ label: 'Todos', value: 'all' },
{ label: 'Vendido', value: 'sales' },
{ label: 'Departamento', value: 'development' },
{ label: 'Administrado', value: 'management' },
],
searchQuery: "",
// AgGrid
gridApi: null,
gridOptions: {},
defaultColDef: {
sortable: true,
resizable: true,
suppressMenu: true
},
columnDefs: [
{headerName: 'ID', field: 'id', width: 125, filter: true, checkboxSelection: true, headerCheckboxSelectionFilteredOnly: true, headerCheckboxSelection: true },
{headerName: 'Username', field: 'client_id', filter: true, width: 210, cellRendererFramework: 'CellRendererLink'},
{headerName: 'Email', field: 'email', filter: true, width: 225 },
{headerName: 'Nombre', field: 'name', filter: true, width: 200 },
{headerName: 'Comuna', field: 'cop_id', filter: true, width: 150},
{headerName: 'Role', field: 'role_id', filter: true, width: 150},
{headerName: 'Acciones', width: 150, cellRendererFramework: 'CellRendererActions'},
],
// Cell Renderer Components
components: {
CellRendererLink,
CellRendererStatus,
CellRendererVerified,
CellRendererActions,
}
}
},
watch: {
roleFilter(obj) {
this.setColumnFilter("role", obj.value)
},
statusFilter(obj) {
this.setColumnFilter("status", obj.value)
},
isVerifiedFilter(obj) {
let val = obj.value === "all" ? "all" : obj.value == "yes" ? "true" : "false"
this.setColumnFilter("is_verified", val)
},
departmentFilter(obj) {
this.setColumnFilter("department", obj.value)
},
},
computed: {
usersData() {
return this.users.data
},
paginationPageSize() {
if(this.gridApi) return this.gridApi.paginationGetPageSize()
else return 10
},
totalPages() {
if(this.gridApi) return this.gridApi.paginationGetTotalPages()
else return 0
},
currentPage: {
get() {
if(this.gridApi) return this.gridApi.paginationGetCurrentPage() + 1
else return 1
},
set(val) {
this.gridApi.paginationGoToPage(val - 1)
}
}
},
methods: {
setColumnFilter(column, val) {
const filter = this.gridApi.getFilterInstance(column)
let modelObj = null
if(val !== "all") {
modelObj = { type: "equals", filter: val }
}
filter.setModel(modelObj)
this.gridApi.onFilterChanged()
},
resetColFilters() {
// Reset Grid Filter
this.gridApi.setFilterModel(null)
this.gridApi.onFilterChanged()
// Reset Filter Options
this.roleFilter = this.statusFilter = this.isVerifiedFilter = this.departmentFilter = { label: 'All', value: 'all' }
this.$refs.filterCard.removeRefreshAnimation()
},
updateSearchQuery(val) {
this.gridApi.setQuickFilter(val)
}
},
created(){
//let users = /apps/user/user-list/
axios.get('api/users')
.then(res => {
let users = res.data.users;
})
}
}
</script>
<style lang="scss">
#page-user-list {
.user-list-filters {
.vs__actions {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-58%);
}
}
}
</style>

Creating An Association between 2 models In Express-Sequelize MySql

Disclaimer: I am very new to
Node/Express/Sequelize
Questions:
1. Do I need to import visitors.js to visitorsInfo.js so that I can create an association between the 2?
2. If not, how do I set up the visitorsInfo_id as a Foreign Key from visitors.js column visitors_id?
Snippet:
...model/visitors.js
'use strict'
module.exports = ( sequelize , type ) => {
return sequelize.define( 'visitors' , {
visitor_id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true
},
web_status: {
type: type.BOOLEAN
},
digital_status: {
type: type.BOOLEAN
},
hosting_status: {
type: type.BOOLEAN
},
training_status: {
type: type.BOOLEAN
},
})
}
.../model/visitors_info.js
'use strict'
module.exports = ( sequelize , type) => {
return sequelize.define( 'user_info' , {
visitorsInfo_id: {
type: type.INTEGER,
/*
How to set up foreign key...?
*/
},
firstname: {
type: type.STRING
},
lastname: {
type: type.STRING
},
company: {
type: type.STRING
},
contact_info: {
type: type.INTEGER
}
})
}
No need import visitors.js to visitorsInfo.js
Base on the document from Sequelize, In file visitorsInfo.js
'use strict'
module.exports = ( sequelize , type) => {
var user_info = sequelize.define( 'user_info' , {
visitorsInfo_id: {
type: type.INTEGER,
},
firstname: {
type: type.STRING
},
lastname: {
type: type.STRING
},
company: {
type: type.STRING
},
contact_info: {
type: type.INTEGER
}
});
user_info.associate = function (models) {
// associations can be defined here
user_info.belongsTo(models.visitors, {
as: 'visitors',
foreignKey: 'visitorsInfo_id',
targetKey: 'visitor_id'
});
}
return user_info
}

Javascript `this` changes constantly, don't know why

Currently, I'm writing an app on Node.js 5.2.0 on a Linux box with Redis and Caminte. When trying to add different prototype methods to a database object, the context of what this refers to constantly shifts within our reference. After calling push in modelRules.js, this shifts types. I was looking for some assistance with:
How to consistently reference the instantiation of a specific module (function that accepts a schema object) outside of the module itself. I want to tack on prototype functions such as addModelBelongsTo to a User object, and sadly my function simply seems to break when referencing the internal modifiable data members within the class.
The proper organization of the prototype accessors. Is there a specific style that should be used when referencing the insides of the instantiations of these classes?
Why the instantiation of the class User persists data across multiple instantiations of the class? For self[restructuredModelName] (type of array), whenever I call this method on one instantiation, another instantiation of the other object already contains the data of the first instantiation. This should not be happening.
User.js
module.exports = function (schema) {
const IBANVerificationStatusSymbol = Symbol('IBANVerificationStatus');
const relationalMapper = require('./../relationalMapper');
const userObj = {
id: { type: schema.Number },
firstName: { type: schema.String },
lastName: { type: schema.String },
IBAN: { type: schema.String, unique: true },
IBANVerified: { type: schema.Boolean, default: false },
IBANVerificationCode: { type: schema.String },
BIC: { type: schema.String },
email: { type: schema.String, index: true, unique: true },
password: { type: schema.String },
status: { type: schema.Number, default: 0 },
profilePicture: { type: schema.String },
phone: { type: schema.String, index: true, unique: true },
accessToken: { type: schema.String },
prefix: { type: schema.String, default: '+31' },
updated: { type: schema.Date, default: Date.now() },
created: { type: schema.Date, default: Date.now() },
lastAccessedFeed: { type: schema.Date },
street: { type: schema.String },
streetNumber: { type: schema.String },
postalCode: { type: schema.String },
city: { type: schema.String },
country: { type: schema.String },
FCR: { type: schema.Number, default: 0 },
};
// There's GOTTA be a way to typecheck at compilation
const associationMap = {
Activity: { belongsTo: 'Activity', hasMany: 'activities' },
Client: { belongsTo: null, hasMany: 'clients' },
Group: { belongsTo: 'Group', hasMany: 'groups' },
Mandate: { belongsTo: null, hasMany: 'mandates' },
Transaction: { belongsTo: null, hasMany: 'transactions' },
Update: { belongsTo: null, hasMany: 'updates' },
Reaction: { belongsTo: null, hasMany: 'reactions' },
};
relationalMapper.createRelations(associationMap, userObj, schema);
const User = schema.define('user', userObj, {
});
const setId = function (self, models) {
// self.addClients(1);
};
User.hooks = {
afterInitialize: [setId],
};
User.prototype.obj = userObj;
User.associationMap = associationMap;
User.prototype.associationMap = associationMap;
return User;
};
modelRules.js:
function addModelBelongsTo(modelName, models, modelObjKey, modelRelated) {
const restructuredModelName = `memberOf${modelName}`;
const restructuredModelNameCamel = `addMemberOf${modelName}`;
const currentModels = models;
currentModels[modelObjKey].prototype[restructuredModelNameCamel] = function(modelId) {
const self = this;
return new Promise((resolve, reject) => {
if (self[restructuredModelName].indexOf(modelId) <= -1) {
modelRelated.exists(modelId, function(err, exists) {
if (err || !exists) { reject(new Error(err || 'Doesnt exist')); }
console.log(`This:${self}\nrestructuredModelName:${JSON.stringify(self[restructuredModelName])}`);
self[restructuredModelName].push(modelId);
console.log(`This:${self}\nrestructuredModelName:${restructuredModelName}`);
self.save((saveErr) => {
saveErr ? reject(new Error(saveErr)) : resolve(self);
});
});
} else {
reject(new Error(''));
}
});
};
}

scraping data from Javascript in html page

The snippet below is part of an HTML page. I need to scrape the data but not sure what would be the most reliable way. The best way would be JSON, but I'm not sure if the following can be converted to JSON. Is Regular Expression my only choice?
<script type="text/javascript">
window.arMailRuMessages = [];
arMailRuMessages = (function() {
var k = 1024,
u = ajs.Html.unescape,
m = function(data) {
try {
return u(decodeURIComponent(data.text));
} catch (e) {}
return '';
};
return [
{
id: "14412430340000000392",
prev: "",
next: "14412428590000000596",
subject: u("hi"),
date: "1441243034",
size: "3" | 0,
folder: "0",
correspondents: {
from: [{
name: u("firstname lastname"),
email: u("firstname#gmail.com"),
avatars: {
"default": u("\/\/filin.mail.ru\/pic?email=firstname%40gmail.com&trust=true&user=firstname%40mail.ru&sign=CA0D4E8E74E806A459EA9C793CE8BC665EB2D049")
}
}],
to: [{
name: u(""),
email: u("firstname6000#mail.ru"),
avatars: {
"default": u("")
}
}],
cc: []
},
flags: {
spf: true,
unread: true,
flagged: false,
reply: false,
forward: false,
attach: false
},
snippet: m({
"ntype": "letter",
"text": "thisisaford"
}),
priority: 3
}, {
id: "14412428590000000596",
prev: "14412430340000000392",
next: "",
subject: u("hi"),
date: "1441242859",
size: "3" | 0,
folder: "0",
correspondents: {
from: [{
name: u("firstname lastname"),
email: u("firstname#gmail.com"),
avatars: {
"default": u("\/\/filin.mail.ru\/pic?email=firstname%40gmail.com&trust=true&user=firstname%40mail.ru&sign=CA0D4E8E74E806A459EA9C793CE8BC665EB2D049")
}
}],
to: [{
name: u(""),
email: u("firstname#mail.ru"),
avatars: {
"default": u("")
}
}],
cc: []
},
flags: {
spf: true,
unread: true,
flagged: false,
reply: false,
forward: false,
attach: false
},
snippet: m({
"ntype": "letter",
"text": "thisisatest"
}),
priority: 3
}
];
})();
__log.letters_data_js = 1;
</script>
With HtmlUnit, you can use htmlPage.executeJavaScript, which will return an Object to manipulate.
Below is a complete example:
try (final WebClient webClient = new WebClient(BrowserVersion.CHROME)) {
String url = "http://localhost/test.html";
HtmlPage htmlPage = webClient.getPage(url);
NativeArray array = (NativeArray) htmlPage.executeJavaScript("arMailRuMessages").getJavaScriptResult();
for (int i = 0; i < array.getLength(); i++) {
NativeObject object = (NativeObject) array.get(i);
String id = (String) object.get("id");
System.out.println(id);
NativeObject correspondents = (NativeObject) object.get("correspondents");
NativeArray from = (NativeArray) correspondents.get("from");
System.out.println(((NativeObject) from.get(0)).get("name"));
}
}

Categories