Mock JS object in tests on browser platform - flutter

I have this simplified dart file using dart:js:
(dataLayer is used for Google tags, if that's any help)
#JS()
import 'package:js/js.dart';
#JS('dataLayer.push')
external void _push(data);
class Manager {
void pushEvent(String event) {
_push(event);
}
}
On the web, it runs correctly and the dataLayer object is being created in a script in the web/index.html file.
I am trying to write a test about it. I would like to verify dataLayer.push is being called with the correct parameters.
I run my test with the command
flutter test --platform chrome
But I get this error:
TypeError: Cannot read properties of undefined (reading 'push')
Is it possible to create a dummy dataLayer variable (and maybe have the hand on it to record the calls to the method .push())? If yes, how?
Here is my attempt:
#TestOn('browser')
import 'package:flutter_test/flutter_test.dart';
import 'package:js/js.dart';
import 'my_project/my_file.dart';
class _DataLayer {
void push(dynamic data) {}
}
#JS('dataLayer')
final dataLayer = _DataLayer();
void main() {
test('It should push the event', () {
Manager().pushEvent('myEvent');
});
}

A way to do it would be to load and use a custom HTML file during the tests where you can include a script to create the js variables you need.
Follow the instructions of the package test.
If your test file name is folder/my_test.dart, then you can create a html file named (folder/my_test.html):
<!doctype html>
<html>
<head>
<title>Custom HTML file title</title>
<link rel="x-dart-test" href="my_test.dart">
<script src="packages/test/dart.js"></script>
<script>
window['dataLayer'] = [];
</script>
</head>
</html>
See this StackOverflow answer and this StackOverflow question.
and then you can run
dart test --platform chrome
However, this is only supported with dart test and not flutter test, see this issue. In it, they recommend writing an integration test instead.

Related

How to dynamically import a json file

I'm trying to import a JSON file on my Vite app, whose paths are dynamically generated. So the import path for this JSON includes variables.
I know it's possible to do it with require, but I'm working with Svelte, and I cant use requires.
You can use a dynamic import() statement for that. This will return a promise, which has to be awaited, though. E.g.
<script lang="ts">
import meta from './meta.json';
const filePromise = import(/* #vite-ignore */ `./${meta.file}.json`);
</script>
{#await filePromise then file}
{file.property}
{/await}
There are some limitations to dynamic imports in Vite so the application can be built properly. The #vite-ignore comment silences a warning output about these limitations.

Access --dart-define environment variables inside index.html

Is there any way to access Environment Variables defined by the --dart-define command inside the index.html file of Flutter Web?
I currently can access them inside iOS and Android native files but have not found a way to do so inside the html file
Access to the environment declarations (this is the most correct name, also used in the doc of the String.fromEnvironment() method; see also
dart-sdk issue #42136 - Clarify usage of -D/environment variables/environment declarations), is also possible from the javascript code.
There are two details to keep in mind:
String.fromEnvironment() can only be invoked with const (also implicit, in const context) and never with "new".
In Flutter/web, the main() method is not executed immediately upon loading the main.dart.js script, so it is not sufficient to place the js script (which reads the variable declared in dart) immediately after main.dart.js. It is therefore necessary to signal in some way to the js code when the dart code has been executed. To solve this problem, I resort to a custom DOM event. If there are better solutions, I invite you to report them.
Example:
main.dart
import 'package:flutter/material.dart';
import 'dart:js' as js;
import 'dart:html' as html;
void main() {
//To expone the dart variable to global js code
js.context["my_dart_var"] = const String.fromEnvironment("my_dart_var");
//Custom DOM event to signal to js the execution of the dart code
html.document.dispatchEvent(html.CustomEvent("dart_loaded"));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
//...
}
In index.html:
<script src="main.dart.js" type="application/javascript"></script>
<script>
//Here my_dart_var is undefined
console.log(`my_dart_var: ${window.my_dart_var}`);
document.addEventListener("dart_loaded", function (){
//Here my_dart_var is defined
console.log("dart_loaded event");
console.log(`my_dart_var: ${window.my_dart_var}`);
});
</script>

How to execute browser method inside protractor custom locator?

I want to execute browser method inside the protractor custom locator like:
import {browser} from 'protractor';
protractor.by.addLocator("demo",(selector: string) => {
browser.executeScript('my script')
});
This throws error like:
protractor_1 not defined.
Any help will be valuable.
Add on: Let me know, if we can use async /await inside custom locator.
The problem is CLEARLY NOT in the code you posted.
Somewhere you're using protractor_1, which is not defined. Look for it in your code
You have imported 'browser' but not the actual 'protractor'
import { browser, protractor } from 'protractor';
browser.executeScript cannot be executed in the context of the browser.
"#param {Function|string} script. A script to be run in the context of the browser.
Follow up the documentation: https://www.protractortest.org/#/api?view=ProtractorBy.prototype.addLocator

(How) can I use System.import in babel along with require.js?

I'm playing a little bit with the new ES6 functionalities and Babel. I'm successfully using the modules export/import functionalities by means of require.js (transpiling into AMD), but the experimental module loader doesn't want to work. Here is my code and configurations:
extract of front-app/tst.js
import {tstimp as functocall} from "front-app/tstimp.js";
...
/**
* LOADING MODULES DYNAMICALLY
*/
System.import('front-app/tst_dyn_mod')
.then(some_module => {
console('using the module!');
some_module.sayHello();
})
.catch(error => {
console.log('error!');
console.log(error);
});
My .babelrc looks like this:
{
"presets": ["es2015", "react"],
"plugins": ["transform-es2015-modules-amd"]
}
and the scripts I import are these ones, in that order:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="node_modules/babel-polyfill/dist/polyfill.min.js"></script>
<script data-main="front-app/tst" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.8/require.min.js"></script>
<script src="node_modules/es6-module-loader/dist/es6-module-loader.js"></script>
</head>
<body></body>
</html>
Unfortunately what I get is the following error by using firefox:
error! tst.js:695:9
Error: a is undefined
Error loading http://localhost/es6r1/front-app/tst_dyn_mod
what's that a? Am I missing something? Keep in mind my code is transpiled into AMD, but System is supposed to stay there in the transpiled code (and it IS there). The polyfill should do the dirty job, right?
I successfully get the thing working on Babel 6 with a slighlty different config (babel-node cli & thus commonjs) thanks to this plugin: https://www.npmjs.com/package/babel-plugin-system-import (npm install babel-plugin-system-import-transformer). Here's a excerpt of my .babelrc:
…
"plugins": [
"system-import-transformer",
{
"modules": "common"
}
]
…
Setting amd instead of common like indicated in the documentation should do the trick for you.
Only limitation of this tiny plugin, you should get a plain string module name like System.import("plainString") and not a computed one (nor string concatenation with +, nor new ES6 template literal and neither variable name). It seemds linked to that line of code.
I will try to PR a fix on that limitation if I can.
Just an update on this, https://github.com/thgreasi/babel-plugin-system-import-transformer has support for non string parameters. Just make sure not to use the updated alias package.

Typescript import class

I have been trying to get this thing to work for quite a while now, and so far no solution that I have found has worked for me.
This is what I have:
//Test.ts
module t
{
export class Test
{
constructor ()
{
alert("test");
}
}
}
//Main.ts
/// <reference path="Test.ts" />
var test: t.Test = new t.Test();
As you can see I have two files, one is Main.ts and the other one is Test.ts. They are both in the same folder. Also if it helps I am using VS2012 and the Typescript plugin.
Thank you for the help!
EDIT: Oh btw, I get the error (in chrome): Uncaught ReferenceError: t is not defined
You need to include both scripts in your web page:
<script src="test.js"></script>
<script stc="main.js"></script>