I'm trying to setup intern for my project, a Dojo/JS project, and the server is not Node... I get a loader issue, which seems to be due to dojo.has using Dojo loader... The require wrapper suggested in here did not work for me.
I get the error below:
> node node_modules/intern/client.js config=tests/intern
Defaulting to "console" reporter
dojo/text plugin failed to load because loader does not support getText
TypeError: undefined is not a function
at Object.load (lib/dojo/dojo/text.js:199:6)
Below are my intern configuration and the test file:
/tests/intern.js: (config file)
loader: {
packages: [ { name: 'visitorsPortal', location: 'portals/visitor' },
{ name: 'dojo', location: 'lib/dojo/dojo'},
{ name: 'dijit', location: 'lib/dojo/dijit'},
{ name: 'portalLib', location: 'portals/lib'} ]
},
suites: [ 'tests/uitests' ],
tests/uitests:
define([
'intern!tdd',
'intern/chai!assert',
'portals/visitor/views/MyModule'
], function (test, assert, MyModule) {
// empty for now...
});
This has nothing to do with dojo/has and everything to do with the dojo/text plugin requiring functionality that only exists within the Dojo 1 loader when being used server-side.
If you are attempting to test software that relies on any non-standard AMD loader functionality, you will need to use the non-standard loader, or override those modules with alternative copies that are compatible with other loaders.
In this specific case, your easiest path forward is to use the geezer edition of Intern, since it includes the old Dojo loader which contains these non-standard extensions. The best path forward is to remap the dojo/text module to another compatible module that does not need anything special in the loader in order to retrieve the data:
// in intern.js
// ...
loader: {
map: {
'*': {
'dojo/text': 'my/text'
}
}
},
// ...
I struggled with the same problem yesterday, but thanks to C Snover's answer here and the question you're linking to, I did make some progress.
I added the map directive to the intern.js loader config (as C Snover suggests).
// in intern.js
// ...
loader: {
map: {
'*': {
'dojo/text': 'my/text'
}
}
},
// ...
For the my/text module, I just copied dojo/text and added an else if clause to the part that resolves the getText function:
if(has("host-browser")){
getText= function(url, sync, load){
request(url, {sync:!!sync}).then(load);
};
} else if(has("host-node")){
// This was my addition...
getText = function(url, sync, load) {
require(["dojo/node!fs"], function(fs) {
fs.readFile(url, 'utf-8', function(err, data) {
if(err) console.error("Failed to read file", url);
load(data);
});
});
};
} else {
// Path for node.js and rhino, to load from local file system.
// TODO: use node.js native methods rather than depending on a require.getText() method to exist.
if(require.getText){
getText= require.getText;
}else{
console.error("dojo/text plugin failed to load because loader does not support getText");
}
}
However, even though the tests were running in intern via node, the host-node value wasn't being set. That was fixed by setting dojo-has-api in my intern.js config:
define(["intern/node_modules/dojo/has"], function(has) {
has.add("dojo-has-api", true);
return { /* my intern config as normal */ };
});
I'll admit I don't understand 100% what I've done here, and with the copy/pasting it's not exactly pretty, but it serves as a temporary fix for my problem at least.
Note: This did introduce another set of issues though: Since Dojo now knows that it's running in node, dojo/request no longer tries to use XHR. I was using sinon.js to mock my xhr requests, so this had to be changed.
Related
I'm tying to follow the official nuxt.js documentation to dynamically load dozens of .mp3 files as the user progresses through a quiz.I've set up my config file as follows:
nuxt.config.js (Nuxt v2.15.8):
build: {
extend ( config, ctx ) {
// Might be used for loading media files
config.module.rules.push({
test: /\.(ogg|mp3|wav|mpe?g)$/i,
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
});
}
}
This works:
import audioFile from '~/assets/audio/sfx_Q1_1.mp3';
// Outputs /_nuxt/assets/audio/sfx_Q1_1.mp3, and the file plays as expected
console.log(audioFile);
Using an import statement works, and it gives me the correct asset path. The problem is that I need dozens of mp3s with sfx_Q#_#.mp3 format, so I need a more dynamic approach.
This doesn't work:
mounted() {
let audioURL = `~/assets/audio/sfx_Q${this.qID}_${this.aID}.mp3`;
let audio = require(audioURL);
console.log(audio);
}
With this approach, my app crashes and I get Cannot find module '~/assets/audio/sfx_Q1_1.mp3', even though it's exactly the same path as the version that worked with the import statement.
This also works
<audio v-bind:src="require(`~/assets/audio/sfx_Q${qID}_${aID}.mp3`).default"></audio>
...but this doesn't work
<audio v-bind:src="require(getAudioURL()).default"></audio>
// ... //
getAudioURL() {
return `~/assets/audio/sfx_Q${this.qID}_${this.aID}.mp3`;
}
... I get the same Cannot find module error. Why does the import break when the file path is generated dynamically inside a method? I've tried with # symbol as well, to no avail. This question had the same issue, but I've tried exactly the solutions suggested, and it doesn't help.
I get an error porting from Vue.js to Nuxt.js.
I am trying to use vue-session in node_modules. It compiles successfully, but in the browser I see the error:
ReferenceError window is not defined
node_modules\vue-session\index.js:
VueSession.install = function(Vue, options) {
if (options && 'persist' in options && options.persist) STORAGE = window.localStorage;
else STORAGE = window.sessionStorage;
Vue.prototype.$session = {
flash: {
parent: function() {
return Vue.prototype.$session;
},
so, I followed this documentation:
rewardadd.vue:
import VueSession from 'vue-session';
Vue.use(VueSession);
if (process.client) {
require('vue-session');
}
nuxt.config.js:
build: {
vendor: ['vue-session'],
But I still cannot solve this problem.
UPDATED AUGUST 2021
The Window is not defined error results from nodejs server side scripts not recognising the window object which is native to browsers only.
As of nuxt v2.4 you don't need to add the process.client or process.browser object.
Typically your nuxt plugin directory is structured as below:
~/plugins/myplugin.js
import Vue from 'vue';
// your imported custom plugin or in this scenario the 'vue-session' plugin
import VueSession from 'vue-session';
Vue.use(VueSession);
And then in your nuxt.config.js you can now add plugins to your project using the two methods below:
METHOD 1:
Add the mode property with the value 'client' to your plugin
plugins: [
{ src: '~/plugins/myplugin.js', mode: 'client' }
]
METHOD 2: (Simpler in my opinion)
Rename your plugin with the extension .client.js and then add it to your plugins in the nuxt.config.js plugins. Nuxt 2.4.x will recognize the plugin extension as to be rendered on the server side .server.js or the client side .client.js depending on the extension used.
NOTE: Adding the file without either the .client.js or .server.js extensions will render the plugin on both the client side and the server side. Read more here.
plugins: ['~/plugins/myplugin.client.js']
There is no window object on the server side rendering side. But the quick fix is to check process.browser.
created(){
if (process.browser){
console.log(window.innerWidth, window.innerHeight);
}
}
This is a little bit sloppy but it works. Here's a good writeup about how to use plugins to do it better.
Its all covered in nuxt docs and in faq. First you need to make it a plugin. Second you need to make your plugin client side only
plugins: [
{ src: '~/plugins/vue-notifications', mode: 'client' }
]
Also vendor is not used in nuxt 2.x and your process.client not needed if its in plugin with ssr false
In Nuxt 3 you use process.client like so:
if (process.client) {
alert(window);
}
If you've tried most of the answers here and it isn't working for you, check this out, I also had the same problem when using Paystack, a payment package. I will use the OP's instances
Create a plugin with .client.js as extension so that it can be rendered on client side only. So in plugins folder,
create a file 'vue-session.client.js' which is the plugin and put in the code below
import Vue from 'vue'
import VueSession from 'vue-session'
//depending on what you need it for
Vue.use(VueSession)
// I needed mine as a component so I did something like this
Vue.component('vue-session', VueSession)
so in nuxt.config.js, Register the plugin depending on your plugin path
plugins:[
...
{ src: '~/plugins/vue-session.client.js'},
...
]
In index.vue or whatever page you want to use the package... import the package on mounted so it is available when the client page mounts...
export default {
...
mounted() {
if (process.client) {
const VueSession = () => import('vue-session')
}
}
...
}
You can check if you're running with client side or with the browser. window is not defined from the SSR
const isClientSide: boolean = typeof window !== 'undefined'
Lazy loading worked for me. Lazy loading a component in Vue is as easy as importing the component using dynamic import wrapped in a function. We can lazy load the StepProgress component as follows:
export default {
components: {
StepProgress: () => import('vue-step-progress')
}
};
On top of all the answers here, you can also face some other packages that are not compatible with SSR out of the box and that will require some hacks to work properly. Here is my answer in details.
The TLDR is that you'll sometimes need to:
use process.client
use the <client-only> tag
use a dynamic import if needed later on, like const Ace = await import('ace-builds/src-noconflict/ace')
load a component conditionally components: { [process.client && 'VueEditor']: () => import('vue2-editor') }
For me it was the case of using apex-charts in Nuxt, so I had to add ssr: false to nuxt.config.js.
With dojo, patch files can be created and loaded via 'patches/patch!' where patch.js contains load function with all patches.
Content patch.js
define(['require'], function(require) {
return {
load: function load(id, parentRequire, loaderCallback) {
require([< patch files >], loaderCallback);
}
};
});
When running intern suites I want that those patches are also loaded first before running my suites.
Is there a way to ensure that my patch files are loaded before running my test suites.
Regards Marco
When you define your tests suite, you have a before function where you can call your load patch function
define(["intern!tdd", "patch"], function (tdd, patch) {
tdd.describe("Tests suite", function () {
tdd.before(function () {
// executes before suite starts
patch.load();
});
});
});
I read this http://webdriver.io/guide/testrunner/cloudservices.html, but not find how launch the tunnel inside the wdio.conf.js and how integrate.
Update:
Now webdriver.io latest version have documentation to integrate the cloud testing services.
See to more:
http://webdriver.io/guide/testrunner/cloudservices.html
Fomerly to integrate the sauce-connect-launcher you need the wdio.conf.js file, and inside the file configure the sauce-connect-launcher to launch the tunnel, example wdio.conf.js file:
var sauceConnectLauncher = require('sauce-connect-launcher');
global.sauceConnectProcess = null;
exports.config = {
//
// ==================
// Specify Test Files
// ==================
// Define which test specs should run. The pattern is relative to the directory
// from which `wdio` was called. Notice that, if you are calling `wdio` from an
// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
// directory is where your package.json resides, so `wdio` will be called from there.
//
user: 'the_pianist2',
key: '27dde83a-1cf7-450d-8c88-857c4d3cde43',
specs: [
//command line
//'spec/**/*.js' wdio wdio.conf.js
//grunt
'./www-test/e2e/spec/*.js'
],
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
],
//
// ============
// Capabilities
// ============
// Define your capabilities here. WebdriverIO can run multiple capabilties at the same
// time. Depending on the number of capabilities, WebdriverIO launches several test
// sessions. Within your capabilities you can overwrite the spec and exclude option in
// order to group specific specs to a specific capability.
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:
// https://docs.saucelabs.com/reference/platforms-configurator
//
capabilities: [{
browserName: 'chrome'
},
{
browserName: 'firefox'
}
],
//
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
//
// Level of logging verbosity.
//logLevel: 'result',
//
// Enables colors for log output.
coloredLogs: true,
//
// Saves a screenshot to a given path if a command fails.
screenshotPath: './errorShots/',
//
// Set a base URL in order to shorten url command calls. If your url parameter starts
// with "/", the base url gets prepended.
//desarrollo
baseUrl: 'http://localhost:3001',
//produccion
//baseUrl: 'http://www.example.com',
//
// Default timeout for all waitForXXX commands.
waitforTimeout: 20000,
//
// Initialize the browser instance with a WebdriverIO plugin. The object should have the
// plugin name as key and the desired plugin options as property. Make sure you have
// the plugin installed before running any tests. The following plugins are currently
// available:
// WebdriverCSS: https://github.com/webdriverio/webdrivercss
// WebdriverRTC: https://github.com/webdriverio/webdriverrtc
// Browserevent: https://github.com/webdriverio/browserevent
// plugins: {
// webdrivercss: {
// screenshotRoot: 'my-shots',
// failedComparisonsRoot: 'diffs',
// misMatchTolerance: 0.05,
// screenWidth: [320,480,640,1024]
// },
// webdriverrtc: {},
// browserevent: {}
// },
//
// Framework you want to run your specs with.
// The following are supported: mocha, jasmine and cucumber
// see also: http://webdriver.io/guide/testrunner/frameworks.html
//
// Make sure you have the node package for the specific framework installed before running
// any tests. If not please install the following package:
// Mocha: `$ npm install mocha`
// Jasmine: `$ npm install jasmine`
// Cucumber: `$ npm install cucumber`
framework: 'jasmine',
//
// Test reporter for stdout.
// The following are supported: dot (default), spec and xunit
// see also: http://webdriver.io/guide/testrunner/reporters.html
reporter: 'spec',
reporterOptions: {
//
// If you are using the "xunit" reporter you should define the directory where
// WebdriverIO should save all unit reports.
outputDir: './'
},
//
// Options to be passed to Jasmine.
jasmineNodeOpts: {
//
// Jasmine default timeout
defaultTimeoutInterval: 20000,
//
// The Jasmine framework allows it to intercept each assertion in order to log the state of the application
// or website depending on the result. For example it is pretty handy to take a screenshot everytime
// an assertion fails.
expectationResultHandler: function(passed, assertion) {
}
},
//
// =====
// Hooks
// =====
// Run functions before or after the test. If one of them returns with a promise, WebdriverIO
// will wait until that promise got resolved to continue.
//
// Gets executed before all workers get launched.
onPrepare: function() {
return new Promise(function(resolve, reject) {
sauceConnectLauncher({
username: 'the_pianist2',
accessKey: '27dde83a-1cf7-450d-8c88-857c4d3cde43',
}, function (err, sauceConnectProcess) {
if (err) {
return reject(err);
}
console.log('conexion realizada');
global.sauceConnectProcess = sauceConnectProcess
resolve();
});
});
},
//
// Gets executed before test execution begins. At this point you will have access to all global
// variables like `browser`. It is the perfect place to define custom commands.
before: function() {
// do something
},
//
// Gets executed after all tests are done. You still have access to all global variables from
// the test.
after: function(failures, pid) {
// do something
},
//
// Gets executed after all workers got shut down and the process is about to exit. It is not
// possible to defer the end of the process using a promise.
onComplete: function() {
console.log('Test completado');
global.sauceConnectProcess.close(function () {
console.log("Closed Sauce Connect process");
return true;
});
}};
In the hook 'onPrepare' the script to connect tunnel is launch and the inside Promise is very important because wait the callback of connection and run the next steps after finish onPrepare functions:
https://github.com/webdriverio/webdriverio/issues/1062
After that the test is launching on the server saucelabs.
I don't understand how it is possible to test a package that is debugOnly.
My package.js is quite simple :
Package.describe({
name: 'lambda',
version: '0.0.1',
debugOnly: true // Will not be packaged into the production build
});
Package.onUse(function(api) {
api.versionsFrom('1.2.1');
api.addFiles('lambda.js');
api.export("Lambda", 'server');
});
Package.onTest(function(api) {
api.use('tinytest');
api.use('lambda');
api.addFiles('lambda-tests.js', 'server');
});
My lambda-test.js :
Tinytest.add('example', function (test) {
test.equal(Lambda.func(), true);
});
My lambda.js :
Lambda = {
func: function() {
return "Christmas";
}
}
When I run meteor test-packages, it just fails : Lambda is not defined. If I remove the debugOnly: true the test pass. So how can I test my package using tinytest ?
Or this is a bug !
I had the same issue! It turns out the tests are working fine. The Lambda is not getting exported in the project either.
from https://github.com/meteor/meteor/blob/0f0c5d3bb3a5492254cd0843339a6716ef65fce1/tools/isobuild/compiler.js
// don't import symbols from debugOnly and prodOnly packages, because
// if the package is not linked it will cause a runtime error.
// the code must access them with `Package["my-package"].MySymbol`.
Try:
Tinytest.add('example', function (test) {
//also changed expected value from true to Christmas to make test pass
test.equal(Package['lambda']['Lambda'].func(), "Christmas");
//you can use Package['lambda'].Lambda as well, but my IDE complains
});
Now you can do something like this:
if (Package['lambda']) {
console.log("we are in debug mode and we have lamda");
console.log("does this say Christmas? " + Package['lambda']["Lambda"]['func']());
} else {
console.log("we are in production mode, or we have not installed lambda");
}