Grails Gebish Functional Tests for page created with javascript - javascript

i am writing a test class
class ListOfProjectsSpec extends GebReportingSpec{
def "project dashboard filters are instantiated correctly" () {
given:
at ProjectIndexPage
expect:
projectTable != null
}
}
to test functionality of my ProjectIndexPage.
class ProjectIndexPage extends ProjectsCategoryPage{
static at = {
$("title").text() == "Title"
}
static content = {
projectTable {
$("table.dynamic-projectTable")
}
}
}
(i cleaned up a lot of code to show the simplest case only).
complication
the class dynamic-projectTable is added at runtime to the table with jquery/javascript in as an acessor for filters on the page.
<g:javascript>
$(
$('#projectTable').addClass('dynamic-projectTable');
});
</g:javascript>
error
junit.framework.AssertionFailedError: geb.error.RequiredPageContentNotPresent:
The required page content 'projectTable- SimplePageContent (owner: ProjectIndexPage, args: [],
value: null)' is not present
additional info
when looking at the html output of the spock test it is clear that the dynamic-projectTable class is not added (as are all other actions performed by the $() jquery call - i removed them here to make the example more readable)
i tried
// calling the document.ready function explicitly in my test cases
when:
$("document").ready()
enabled javascript
driver = {
HtmlUnitDriver driver = new HtmlUnitDriver();
driver.setJavascriptEnabled(true);
driver
}
but nothing seems to work.
any input?

Pages built by javascript often require Geb to wait for the content to be rendered.
See http://www.gebish.org/async for details.
Basically, I'd try explicitly waiting in your spec code:
waitFor { projectTable }
Waits can also be configured on the page content:
projectTable(wait: true) {
$("table.dynamic-projectTable")
}

i was able to solve it by explicityly re-enabling javascript in the testSpec:
def setup() {
if (driver instanceof HtmlUnitDriver) {
driver.javascriptEnabled = true
}
}

To avoid issues like this it is better to define more applicable at checks.
In your example you have used:
static at = {
$("title").text() == "Title"
}
Try checking for something which will be used in the tests. E.g A form element. I would also use a waitFor closure in the at checker so all pages after a to call will be ready for testing.
static at = {
waitFor { $('#projectTable').#class.contains("dynamic-projectTable") }
}

Related

Vaadin 14 Springboot javascript does not work

I'm trying to develop my first Vaadin 14 application, I'm also using Spring boot framework for it.
I've created a javascript file, I put it into the folder frontend/src with the name of teams.js and I'm trying to import it with #JsModule("src/teams.js"). My root view class looks like this:
#Route("")
#PageTitle("Teams organization store init")
#Slf4j
#JsModule("src/teams.js")
#Tag("TeamsEntry")
public class TeamsEntryView extends VerticalLayout implements BeforeEnterObserver {
public TeamsEntryView() {
initTeams();
}
private void initTeams() {
var ui = UI.getCurrent();
var page = ui.getPage();
log.info("Teams initialization...");
page.executeJs(getTeamsConfig()).toCompletableFuture().whenComplete(((jsonValue, throwable) ->
log.info("Teams initialization completed: {} with throwable {}", jsonValue.toJson(), throwable.getMessage())
));
}
private String getTeamsConfig() {
return """
console.log('ss');
window.initTeams();
console.log('xx');
return true;
""";
}
...
My js file looks like this:
window = {
initTeams: function () {
console.log('initTeams...');
}
}
In this case, I see "ss" in the browser's console, but nothing more.
If I remove the window.initTeams(); line I get "ss" and "xx" as well.
If I declare a simple function in the js file and call it without the "window" class I get similar results.
If I use #Javascript or page.addJavascript("src/teams.js") I get this error when the page loads: "> unexpected character"
If I try to call join() or get() on the completable future the browser freeze.
If I use #Javascript("frontend://src/teams.js") I get some CORS error like if it is trying to download something from the "frontend" host.
I've tried to put the #JsModule on a component instead of my view.. it does not work.
I've tried to put my js file to the root, and to the resources folder.
I could not find any other solution to import and use my js file into vaadin14 with spring boot.
Also, I'm not sure why the browser freeze if I call "join()" on completable future, and also the on the result of it sentToBrowser returns false even if I see the console logs in the browsers log...
Can somebody explain to me how should I do the javascript import, why my current code does not work, and why the "join()" freezes the browser, please?
Thank you in advance!
#Edit
I have also tried with this annotation #JavaScript("./src/teams.js") and a js like this:
function initTeams () {
console.log('initTeams...');
console.log("Teams initialized!")
}
#Edit
Okay so finally I got it working.
The js file has to be under root/src/main/webapp/src folder.
Nor #JavaScript and nor the #JsModule worked for me, so I had to import my js file as:
var ui = UI.getCurrent();
var page = ui.getPage();
page.addJavaScript("src/teams.js");
Then I can call it like window.initTeams() or like initTeams() and both works fine. Altough the completable future below still never executes, and isSentToBrowser() always returns false.
page.executeJs(getTeamsConfig()).toCompletableFuture().whenComplete(((jsonValue, throwable) ->
log.info("Teams initialization completed: {} with throwable {}", jsonValue.toJson(), throwable.getMessage())
));
I must mention if I start the path with '.' like page.addJavaScript(".src/teams.js"); then it does not find the file.
Does anybody have an answer, why the completable future never executes?
The problem is that the following code redefines the window object:
window = {
initTeams: function () {
console.log('initTeams...');
}
}
Did you meant to add a function to the window object? Like so:
window.initTeams = function () {
console.log('initTeams...');
};
If you want to keep code visually similar to yours:
window = {
...window,
{
initTeams: function() {
console.log('initTeams...');
}
}
}
Other options:
window['initTeams'] = function() {...}
Object.assign(window, {initTeams: function() {...}})
Object.defineProperty(window, 'initTeams', {value: function() {console.log('foo')}});
Gotta love JavaScript...
Also, for more knowledge, the code mentioned in your #Edit section could not be called. Calling initTeams() is equivalent to window.initTeams(). The function must exist in the window object. This is why, for example, you see some custom elements defined like customElements.define(...) and window.customElements.define(...).

webpack doesnt add prototype function to Object from other file

I have a file containing the definition of a Object and in that same file I have a function that is part of this object like so:
export function ARScene(_callbacks) {
this.callbacksObject = _callbacks;
// more fancy code..
}
ARScene.prototype.changeCar = function() {
//some fancy code here
this.loadHDCar(); // THIS LIKE GENERATES A ERROR.
}
now I have a different file containing an other method that is part of the Object called ARScene like so:
import { ARScene } from './arScene';
ARScene.prototype.loadHDCar = function() {
//some more fancy code..
}
What is happening when I build this with webpack and run it in the browser I get the error that this.loadHDCar(); is undefined I guess this happens because webpack doesnt add a file if it is not imported. But how do I make sure that ARScene.prototype.loadHDCar is added to the object in the final output?
I am a complete newbie to webpack and modules. I have found answers on stackoverflow about this but they had slightly different scenarios then me. So their solutions didnt work (or maybe I didnt understand it).
If more context or information is needed please let me know.
How do I make sure that ARScene.prototype.loadHDCar is added to the object in the final output?
You should import it in the arScene module, and you should even create the prototype method in there (where you are defining the class) for visibility.
export function loadHDCar() {
… //some more fancy code
}
import { loadHDCar } from './loadHDCar';
export function ARScene(_callbacks) {
…
}
ARScene.prototype.loadHDCar = loadHDCar;
ARScene.prototype.changeCar = function() {
… // some fancy code here
this.loadHDCar();
};

Access items inside Webpack bundle in browser console

I am using Typescript with Webpack (debug build using source maps). I can access Static class files in the sources tab normally. However, the class name itself is undefined at the global scope.
class SomeStaticClass {
public static doSomething() {
console.log("I just did something!");
}
}
I would like to access / call
SomeStaticClass.doSomething()
from console in the browser (say Google Chrome Inspector Tools).
Assuming that you want to get all files, you can do something like this:
window.SomeStaticClass = require('./path-to-file-with-class').default;
Inspired by workwise's answer
you can call the static method by
console.log(SomeStaticClass.doSomething())
I managed to make it work refer this url
I managed a workaround like the following. In the main exports.tsx (assuming this is the top level export):
import { StaticClassToBeExposed } from '...SomeFile';
if(type of window !== undefined) {
window.StaticClassToBeExposed = StaticClassToBeExposed;
}

Running IAsyncOperation from a Windows Runtime Component using JavaScript

I have a solution that has both a Windows Runtime Component (C#) and a Universal App (JS).
One of my classes in the WRC has the following static function:
public static IAsyncOperation<Project> Import()
{
return System.Threading.Tasks.Task.Run<Project>(async () =>
{
try
{
FileOpenPicker picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.List;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".xml");
StorageFile source = await picker.PickSingleFileAsync();
if (source != null)
{
StorageFile destination = await ApplicationData.Current.RoamingFolder.CreateFileAsync(source.Name, CreationCollisionOption.ReplaceExisting);
await source.MoveAndReplaceAsync(destination);
return await Project.Open(source.DisplayName);
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}).AsAsyncOperation<Project>();
}
I am trying to call this function from JS using:
SignalOne.Data.Project.import().done(function () {
new Windows.UI.Popups.MessageBox("Done").showAsync();
}
However, while the "Done" message appears, the file open dialog does not. If I put a message box as the first line inside the try of the C#, it doesn't display, either.
I know I have an upper-case Import in C# and a lower-case import in JS, but that is how it comes up with Intellisense, and if I change it to upper-case in JS it crashes.
I'm sure I'm missing something small/stupid, but I can't put my finger on it.
Thanks.
As you known, if we want to use the async method in Windows Runtime Components, we should be able to use the WindowsRuntimeSystemExtensions.AsAsyncAction or AsAsyncOperation extension method to wrap the task in the appropriate interface.
You can use .NET Framework tasks (the Task class and generic Task class) to implement your asynchronous method. You must return a task that represents an ongoing operation, such as a task that is returned from an asynchronous method written in C# or Visual Basic, or a task that is returned from the Task.Run method.
For more info, see Asynchronous operations.
Also the FileOpenPicker.PickSingleFileAsync method should be run in UI thread.
In this example, the event is being fired on the UI thread. If you fire the event from a background thread, for example in an async call, you will need to do some extra work in order for JavaScript to handle the event. For more information, see Raising Events in Windows Runtime Components.
So we should be able to use CoreWindow.GetForCurrentThread method get the UI thread before the async Task is run that the async Task is not run on the UI thread.
For example:
var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
var m_dispatcher = window.Dispatcher;
Then we should be able to use the FileOpenPicker.PickSingleFileAsync method in the CoreDispatcher.RunAsync method.
For example:
await m_dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(async () =>
{
var source = await picker.PickSingleFileAsync();
}));

TypeScript file not loaded or something

I get an error in my console when I try to create a type script file from a partial view (MVC .NET) which is loaded by a rest call and appended to a div element.
I get: Uncaught ReferenceError: xyz is not defined.
in my partial view i have below code:
<script type="text/javascript">
$(document).ready(function () {
var model = #Html.GetJson(Model.x);
var view = new xyz.ExceptionView();
view.init();
});
</script>
Edit: I added "#section scripts {}" around my script tag and then i get nothing in my console and no execution or logging from implementation
And I forgot to mention that my TypeScript file is not implementing anything. But here it is.
Is this not a correct way of using typescript?
module xyz {
export class ExceptionView {
constructor() {
debugger;
}
public init = (model: any): void => {
debugger;
}
}
}
As you can't include or reference TypeScript directly in your HTML, I assume you have a script tag that includes the compiled JavaScript? Does the JavaScript emitted from the TypeScript compilation look right? Are you able to step through the final JavaScript code in the browser Web Developer Tools and see where the breakdown is occurring?

Categories

Resources