How setup react-helmet with Server Side Rendering? - node.js

I've been trying to setup react-helmet with server-side-rendering. I followed the docs and some blog posts on how to setup react-helmet with SSR, but have been unable to produce the desired results. Here's a code snippet of how I'm rendering the App:
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './src/App';
const express = require('express');
const app = express();
app.get('*', (req, res) => {
const app = renderToString(<App />);
const helmet = Helmet.renderStatic();
res.send(formatHTML(app, helmet));
})
function formatHTML(appStr, helmet) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
</head>
<body>
<div id="root">
${ appStr }
</div>
<script src="./bundle.js"></script>
</body>
</html>
`
}
When I run the above code, I get an error saying 'cannot use import statement outside a module'. Is it possible to use both es5 and es6 syntax at the same time? Or is there is better way to setup React-helmet?
This is my babel configuration file
{
"presets": [
[
"#babel/preset-env",
{
"modules": false
}
],
"#babel/preset-react",
"#babel/preset-flow"
],
"env": {
"development": {
"only": [
"app",
"internals/scripts"
],
"plugins": [
"#babel/plugin-transform-react-jsx-source"
]
},
"production": {
"only": [
"app"
],
"plugins": [
"transform-react-remove-prop-types",
"#babel/plugin-transform-react-constant-elements",
"#babel/plugin-transform-react-inline-elements"
]
},
"test": {
"plugins": [
"#babel/plugin-transform-modules-commonjs",
"dynamic-import-node"
]
}
},
"compact": true,
"plugins": [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-syntax-import-meta",
"#babel/plugin-proposal-class-properties",
"#babel/plugin-proposal-json-strings",
[
"#babel/plugin-proposal-decorators",
{
"legacy": true
}
],
"#babel/plugin-proposal-function-sent",
"#babel/plugin-proposal-export-namespace-from",
"#babel/plugin-proposal-numeric-separator",
"#babel/plugin-proposal-throw-expressions",
"#babel/plugin-proposal-export-default-from",
"#babel/plugin-proposal-logical-assignment-operators",
"#babel/plugin-proposal-optional-chaining",
[
"#babel/plugin-proposal-pipeline-operator",
{
"proposal": "minimal"
}
],
"#babel/plugin-proposal-nullish-coalescing-operator",
"#babel/plugin-proposal-do-expressions",
"#babel/plugin-proposal-function-bind",
"lodash"
]
}

You need to wrap your server using #babel/register.
This is how I handle that for my TypeScript CRA projects without ejecting.
NOTICE: I use this method to inject metadata into index.html vs render the whole app (some components I use don’t play well with SSR).
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div id="root"></div>
</body>
</html>
index.js
"use strict"
require("ignore-styles")
require("#babel/register")({
ignore: [/(node_modules)/],
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript",
],
extensions: [".tsx"],
cache: false,
})
require("./server")
server.js (excerpt)
const indexPath = path.join(__dirname, "build/index.html")
const middleware = async (req, res, next) => {
let context = {}
let html = renderToString(
React.createElement(StaticRouter, {
location: req.url,
context: context,
})
)
const helmet = Helmet.renderStatic()
if (context.url) {
res.redirect(context.url)
} else if (!fs.existsSync(indexPath)) {
next("Site is updating... please reload page in a few minutes.")
} else {
let index = fs.readFileSync(indexPath, "utf8")
let status = 200
if (typeof context.status === "number") {
status = context.status
}
return res.status(status).send(
index
.replace('<div id="root"></div>', `<div id="root">${html}</div>`)
.replace("</head>", `${helmet.meta.toString()}</head>`)
.replace("</head>", `${helmet.title.toString()}</head>`)
.replace("</head>", `${helmet.script.toString()}</head>`)
)
}
}
server.get("/", middleware)
server.use(express.static(path.join(__dirname, "build")))
server.get("*", middleware)

Related

Stripe checkout works locally but not when i upload it to my site

I'm new to stack overflow. I state that I am more oriented to the front end and it is probably for this reason that I find myself stuck in this thing. I hope you can understand it and help me out. So I'm trying to integrate stripe checkout and, locally, it works. I state that I have installed all the required packages: express, dotenv, nodemon, stripe and cors. To make it work I open the html index with VS code's live server extension. The problems come when I put the site online and they are certainly due to my ignorance. I tried to understand from the web how to proceed but I didn't understand it and/or maybe I'm missing some steps. Do I have to change the origin url of cors and the url of the fetch call? In the hope that I have asked the question correctly and that I have been clear enough, I thank anyone who can help me in advance.
PS: I have two folders one server and one client. In client I have index.html, cancel.html, success.html and script.js (although success and cancel are unused) while in server folder I have server.js file, package json, package-lock json and node_modules folder . Maybe it's superfluous but I thought it was right to specify it
I tried to change the url of cors origin and tried to create an endpoint on stripe https://dashboard.stripe.com/webhooks/create then put it in the fetch call. I expect the integration to work online as well as locally
require("dotenv").config()
let success = "https://example.com/"
let cancel = "https://example.com/"
const express = require("express")
const app = express()
app.use(express.json())
const cors = require('cors')
app.use(
cors({
origin: "http://127.0.0.1:5500",
})
)
const Stripe = require('stripe');
const stripe = Stripe('my_sk_test_');
const storeItems = new Map([
[1, { priceInCents: 100, name: "Learn React Today" }],
[2, { priceInCents: 200, name: "Learn CSS Today" }],
])
app.post("/create-checkout-session", async (req, res) => {
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
mode: "payment",
line_items: req.body.items.map(item => {
const storeItem = storeItems.get(item.id)
return {
price_data: {
currency: "usd",
product_data: {
name: storeItem.name,
},
unit_amount: storeItem.priceInCents,
},
quantity: item.quantity,
}
}),
success_url: `${success}`,
cancel_url: `${cancel}`,
})
res.json({ url: session.url })
} catch (e) {
res.status(500).json({ error: e.message })
}
})
app.listen(3000)
//script.js
const button = document.querySelector("button")
button.addEventListener("click", () => {
fetch("http://localhost:3000/create-checkout-session", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [
{ id: 1, quantity: 3 },
{ id: 2, quantity: 1 },
],
}),
})
.then(res => {
if (res.ok) return res.json()
return res.json().then(json => Promise.reject(json))
})
.then(({ url }) => {
window.location = url
})
.catch(e => {
console.error(e.error)
})
})
<!-- index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://js.stripe.com/v3/" defer></script>
<script src="script.js" defer></script>
</head>
<body>
<button>Checkout</button>
</body>
</html>
<!-- -->
package.json
{
"name": "server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"devStart": "nodemon server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^10.0.0",
"express": "^4.18.2",
"stripe": "^11.8.0"
},
"devDependencies": {
"nodemon": "^2.0.12"
},
"description": ""
}

White screen after building my electron-vue app for production

I am building an electron-vue app with multiple windows, I am using vue-router.
The app works well when running from Visual Studio Code terminal (dev mode), but after building it for production I am getting a white screen.
Here is my code
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>EmaFlow Work Sessiong Tracker</title>
</head>
<body>
<noscript>
<strong>We're sorry but statement-ts doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app" class="h-100"></div>
<!-- built files will be auto injected -->
</body>
</html>
src/App.vue
<template>
<div id="app" class="h-100">
<router-view />
</div>
</template>
src/router.ts
import Vue from 'vue';
import Router from 'vue-router';
import LoginWindow from '#/views/LoginWindow.vue';
import MainWindow from '#/views/MainWindow.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'login',
component: LoginWindow,
},
{
path: '/main',
name: 'main',
component: MainWindow,
},
],
});
src/main.ts
import Vue from 'vue';
import VeeValidate from 'vee-validate';
import VueTimers from 'vue-timers'
import App from './App.vue';
import router from './router';
Vue.use(VeeValidate);
Vue.use(VueTimers)
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App),
}).$mount('#app');
import $ from 'jquery'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
import '#fortawesome/fontawesome-free/js/all.min.js';
src/background.ts
'use strict'
import { app, protocol, BrowserWindow, ipcMain, Event } from 'electron'
import {
createProtocol,
installVueDevtools
} from 'vue-cli-plugin-electron-builder/lib'
const isDevelopment = process.env.NODE_ENV !== 'production'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
const appWindows: BrowserWindow[] = [];
function createWindow(slug: string, options?: object) {
const defaultOptions = {
width: 800,
height: 600,
frame: false,
webPreferences: {
nodeIntegration: true,
},
};
const windowOptions = Object.assign({}, defaultOptions, options);
const window = new BrowserWindow(windowOptions);
appWindows.push(window);
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
window.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string + '/#' + slug);
window.webContents.openDevTools();
} else {
createProtocol('app')
// Load the index.html when not in development
window.loadURL('app://./index.html' + '/#' + slug);
}
window.on('closed', () => {
appWindows.splice(appWindows.indexOf(window), 1);
});
}
function createLoginWindow() {
createWindow('/', {
width: 400,
height: 300,
resizable: isDevelopment,
});
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installVueDevtools();
} catch (e) {
// console.error('Vue Devtools failed to install:', e.toString());
}
}
createLoginWindow();
});
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (appWindows.length === 0) {
createLoginWindow();
}
})
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
ipcMain.on('open-window', (e: Event, arg: WindowParams) => {
createWindow(arg.route, arg.options);
});
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', data => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
package.json
{
"name": "emaflow-worksession-tracker",
"version": "0.1.0",
"private": true,
"scripts": {
"lint": "vue-cli-service lint",
"build": "vue-cli-service electron:build",
"serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
},
"main": "background.js",
"dependencies": {
"axios": "^0.19.0",
"bootstrap": "^4.3.1",
"core-js": "^2.6.10",
"howler": "^2.1.2",
"jquery": "^3.4.1",
"popper.js": "^1.15.0",
"typescript": "^3.6.4",
"vee-validate": "^2.2.15",
"vue": "^2.6.10",
"vue-class-component": "^7.0.2",
"vue-property-decorator": "^8.2.2",
"vue-router": "^3.1.3",
"vue-timers": "^2.0.4"
},
"devDependencies": {
"#fortawesome/fontawesome-free": "^5.11.2",
"#vue/cli-plugin-babel": "^3.12.0",
"#vue/cli-plugin-typescript": "^3.12.0",
"#vue/cli-service": "^3.12.0",
"electron": "^5.0.11",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"vue-cli-plugin-electron-builder": "^1.4.0",
"vue-template-compiler": "^2.6.10"
}
}
On app launch, a login window will be shown, on success login, the login window will close and open another window.
To open a window, in background.ts I created the function createWindow which takes the router path as the first parameter. To create the login window for example I call createWindow('/', options) and to create the main app window after successful login I write createWindow('/main', options).
I think my problem is in window.loadUrl inside createWindow in background.ts, but I am not sure what the correct url should be for production mode.
please advise and thanks in advance.
finally I could make window.loadUrl working for the production version as follows:
createProtocol('app');
window.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true
}));
The above code is working but it only opens the login window which has the path '/' in the vue-router routes list.
To open a window for another route like '/main' I tried to append a hash and the route to the pathname like this:
window.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html#', slug),
protocol: 'file',
slashes: true
}));
but it did not work and on dev tools network tab I see this error:
Name: index.html%23/ Status: (blocked:other)
please advice
EDIT: all worked after adding the hash property to the options object passed to formatUrl instead of appending to the pathname manually:
window.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file',
slashes: true,
hash: slug
}));

"Target container is not a DOM element." When Running Webpack server

I have recently switched from using 'npm start' script that comes with create-react-app to Weback's 'npm run build' and then running the server. After slowly getting all the dependencies working I got my bundle to complete building. However, when I use 'npm start' the server starts and nothing shows up on my website.
I tried inputting <div id="root"> into my .html file in my "public" directory, but it didn't work. I then found out Webpack created a second for me? Either way placing a <div> or even a <script> in either one doesn't work.
I have updated all of my dependencies for react so that the ReactDOM could render peacefully. my React code worked perfectly before I switched to webpack so I'm sure it's not a problem with my index.js and whatever it imports.
I'm convinced it's a problem in webpack, but I've tried plenty of different presets in webpack.config.js and can't seem to find any that works (I have forgotten the ones I've tried so I will take any suggestions).
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: './main.js',
path: path.resolve(__dirname, '/dist')
},
plugins: [new webpack.ProgressPlugin(), new HtmlWebpackPlugin(), new webpack.IgnorePlugin(/^pg-native$/)],
module: {
rules: [
{
test: /.(js|jsx)$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
options: {
plugins: [
'syntax-dynamic-import'
],
presets: [
'#babel/preset-env',
'#babel/preset-react',
{
'plugins': ['#babel/plugin-proposal-class-properties']
}
]
}
},
{
test:/\.(s*)css$/,
use:['style-loader','css-loader', 'sass-loader']
},
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
loader: 'url-loader?limit=100000'
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
priority: -10,
test: /[\\/]node_modules[\\/]/
}
},
chunks: 'async',
minChunks: 1,
minSize: 30000,
name: true
}
},
node: {
fs: 'empty'
}
,
devServer: {
open: true
}
};
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './App.js';
import * as serviceWorker from './serviceWorker';
let state = {};
window.setState = (changes) => {
state = Object.assign({}, state, changes);
ReactDOM.render(
<App {...state}/>,
document.getElementById('root')
);
}
/* eslint no-restricted-globals: 0*/
let initialState = {
name: 'Name Test',
location: location.pathname.replace(/^\/?|\/$/g, "")
};
window.setState(initialState);
serviceWorker.unregister();
<!-- .HTML created by Webpack -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
<!-- .HTML created by create-react-app (before webpack)-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Skedge</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
when I go into the web console this shows up as an error -
Uncaught Invariant Violation: Target container is not a DOM element.
at invariant (webpack:///./node_modules/react-dom/cjs/reactdom.development.js?:55:15)
at Object.render (webpack:///./node_modules/react-dom/cjs/reactdom.development.js?:21151:36)
at eval (webpack:///./src/index.js?:35:50)
at Module../src/index.js (http://localhost:8080/main.js:3970:1)
at webpack_require (http://localhost:8080/main.js:20:30)
at eval (webpack:///multi_(webpack)-dev-server/client?:2:18)
at Object.0 (http://localhost:8080/main.js:4015:1)
at webpack_require (http://localhost:8080/main.js:20:30)
at http://localhost:8080/main.js:84:18
at http://localhost:8080/main.js:87:10
It says it doesn't understand the ReactDOM.render() function.
I even threw in a <h1>Test</h1> to see if it was my code but it still bugged out, so I'm sure it's something in my webpack.config.js that isn't right but I just can't figure out what.
Edit
I gave up on using webpack, I undid my npm eject and went back to just using npm start.

angular-google-signin Module throwing 404

I'm trying to implement google signin for an Angular2 app. I run the following command.
npm i angular-google-signin
I got angular-google-signin folder in node_modules. In index.html added the follwoing script.
<script defer src="https://apis.google.com/js/platform.js"></script>
you can find my code below:
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { FormsModule } from '#angular/forms';
import { HttpModule, Headers } from '#angular/http';
import { GoogleSignInComponent } from 'angular-google-signin';
#NgModule({
imports: [BrowserModule, FormsModule, HttpModule],
declarations: [AppComponent, GoogleSignInComponent],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import { Component } from '#angular/core';
import { GoogleSignInSuccess } from 'angular-google-signin';
#Component({
selector: 'my-app',
template: `
<google-signin
[clientId]="myClientId"
[width]="myWidth"
[theme]="myTheme"
[scope]="myScope"
[longTitle]="myLongTitle"
(googleSignInSuccess)="onGoogleSignInSuccess($event)">
</google-signin>
<urls></urls>`,
providers: [GoogleSignInSuccess]
})
export class AppComponent {
private myClientId: string = 'my-client-id';
onGoogleSignInSuccess(event: GoogleSignInSuccess) {
let googleUser: gapi.auth2.GoogleUser = event.googleUser;
let id: string = googleUser.getId();
let profile: gapi.auth2.BasicProfile = googleUser.getBasicProfile();
console.log('ID: ' +
profile
.getId()); // Do not send to your backend! Use an ID token instead.
console.log('Name: ' + profile.getName());
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="node_modules/typescript/lib/typescript.js"></script>
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script defer src="https://apis.google.com/js/platform.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot').then(null, console.error.bind(console));
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
I didn't get any compilation error. when I run the site I got 404 error saying :
"(SystemJS) XHR error (404 Not Found) loading http://localhost:3000/angular-google-signin".
see image below.
could anyone help me out.

Making grunt-mocha works with requirejs

Running the following Mocha tests in a browser works, but when I use grunt mocha in the command line, I get Warning: PhantomJS timed out. I have turned the gruntfile mocha.options.run to false, because, if true, the requirejs do not have the time to run.
Sadly, I cannot find any complete sample on the matter.
the gruntfile contains:
mocha: {
test: {
src: ['tests/index.html']
},
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Mocha Test</title>
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" type="text/css" charset="utf-8" />
</head>
<body>
<div id="mocha"></div>
<script src="../node_modules/mocha/mocha.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
mocha.setup('bdd');
</script>
<script src="../node_modules/chai/chai.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
var expect = chai.expect;
</script>
<script data-main="requireRunner.js" src="../vendor/require-2.1.9.js"></script>
</body>
</html>
requireRunner:
require.config({
baseUrl: '/',
paths: {
'jquery' : '../vendor/jquery-2.0.3',
'underscore' : '../vendor/underscore-1.5.2',
'backbone' : '../vendor/backbone-1.1.0'
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
}
},
urlArgs: 'bust=' + (new Date()).getTime()
});
require([
'tests/models/todoTest'
], mocha.run
);
todoTest
define(['js/models/todo'], function(Todo) {
describe('Todo', function(){
var todo;
before(function() {
todo = new Todo();
})
it('defaults are ok', function(){
expect(todo.get('title')).to.equal('');
expect(todo.get('completed')).to.be.false;
})
})
})
todo
/*global define*/
define([
'underscore',
'backbone'
], function (_, Backbone) {
'use strict';
var TodoModel = Backbone.Model.extend({
// Default attributes for the todo
// and ensure that each todo created has `title` and `completed` keys.
defaults: {
title: '',
completed: false
}
});
return TodoModel;
});
Multiple little things to make it work properly:
paths seem to be more tolerant on the browser, path problem can occur only on grunt-mocha (this is not a problem, just a reminder)
running grunt mocha is not enough, grunt server mocha is the way, a server has to be up
using options:{log: true} combined with requirejs debugging traces in the index.html is good help
Your post has helped me to set up my grunt-mocha task ! Thx
I had de turn false the mocha.options.run, otherwise grunt-mocha handled no tests (guess requirejs had not time to load as you said)
however I did not have to set up any server to make it works.
Running grunt mocha is plenty enough !
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FdP - Tests</title>
<link rel="stylesheet" href="../app/vendor/mocha/mocha.css"/>
</head>
<body>
<div id="mocha"></div>
<script src="../app/vendor/mocha/mocha.js"></script>
<script src="../app/vendor/chai/chai.js"></script>
<script>
mocha.setup('bdd');
chai.should();
</script>
<script data-main="specRunner.js" src="../app/vendor/requirejs/require.js"></script>
</body>
</html>
my mocha task
mocha: {
test: {
src: ['test/index.html']
}
}
specRunner.js
...
function (testSuite) {
'use strict';
require(testSuite.specs, function () {
mocha.run();
});
...
Grunt-mocha version : 0.4.7
I think may be there is some files include error in you test html file. you can go to folder of your html file, double click the file, if test case can run normally in browser, then grunt-mocha can run it too. So, maybe the problem is something like require.js not found.

Resources