Can't click buttons in the title bar (Electron App) - javascript

I try to build a simple Text Editor using Electron.
At the moment I want to add a custom title bar which doesn't really work as the buttons are not clickable...
I added an onclick tag to the buttons.
main.js:
//-- variables --\\
const { BrowserWindow, app, Menu, dialog, ipcMain } = require("electron")
let window
let filePath = null //currently opened file
let openFiles = [] //opened files
//-- startup --\\
app.whenReady().then(function()
{
createWindow()
createMenu()
})
//-- functions --\\
function createWindow()
{
window = new BrowserWindow
({
width: 1000,
height: 600,
frame: false,
icon: "./assets/icon.png",
darkTheme: true,
autoHideMenuBar: true,
webPreferences:
{
nodeIntegration: true,
contextIsolation: false
}
})
//window.maximize()
window.loadFile("./frontend/index.html")
window.webContents.openDevTools()
}
function createMenu()
{
Menu.setApplicationMenu(Menu.buildFromTemplate
([
{
label: "File",
submenu:
[
{
label: "Save",
click: saveFile,
accelerator: "CmdOrCtrl+S"
},
{
label: "Open",
click: openFile,
accelerator: "CmdOrCtrl+O"
},
{
label: "New",
click: newFile,
accelerator: "CmdOrCtrl+N"
}
]
}
]))
}
async function promptFilePathOpen()
{
await dialog.showOpenDialog
({ properties: ["openFile"] }).then(function(res)
{
if(res.canceled) return
filePath = res.filePaths[0]
})
}
async function promptFilePathSave()
{
await dialog.showSaveDialog().then(function(res)
{
if(res.canceled) return
filePath = res.filePath
})
}
async function openFile()
{
await promptFilePathOpen()
//openFiles.push(filePath)
window.webContents.send("crd-openFile", filePath)
}
async function saveFile()
{
if(filePath == null || undefined) await promptFilePathSave()
window.webContents.send("crd-saveFile", filePath)
}
async function newFile()
{
filePath = null
await promptFilePathSave()
window.webContents.send("crd-resetEditor")
window.webContents.send("crd-saveFile", filePath)
}
//-- listeners --\\
app.on("window-all-closed", function()
{
if(process.platform != "darwin") app.quit()
})
ipcMain.on("crd-minimizeWindow", function() //does not get called by the renderer
{
//coming soon
})
ipcMain.on("crd-toggleWindowSize", function() //does not get called by the renderer
{
//coming soon
})
ipcMain.on("crd-closeWindow", function() //does not get called by the renderer
{
console.log("quit")
})
renderer/script.js:
//-- imports --\\
const { ipcRenderer } = require("electron")
const fs = require("fs")
const editorElem = document.querySelector("textarea.editor")
//-- functions --\\
function minimizeWindow() // does not get called by the button (index.html)
{
ipcRenderer.send("crd-minimizeWindow")
}
function toggleWindowSize() // does not get called by the button (index.html)
{
ipcRenderer.send("crd-toggleWindowSize")
}
function closeWindow() // does not get called by the button (index.html)
{
ipcRenderer.send("crd-closeWindow")
}
//-- listeners --\\
ipcRenderer.on("crd-openFile", function(e, path)
{
editorElem.value = fs.readFileSync(path, "utf-8")
})
ipcRenderer.on("crd-saveFile", function(e, path)
{
fs.writeFile(path, editorElem.value , function(res, err)
{
if(err) throw err
})
})
ipcRenderer.on("crd-resetEditor", function()
{
editorElem.value = ""
})
The entire code is avalable on my GitHub, because I do not want the whole question consisting of code :)

Two issues here:
You defined functions like closeWindow, but you didn't actually add an event listener for them. You mention onclick but I can't see that in your code. So the first step would be to add document.querySelector('.closeWindow').addEventListener('click', closeWindow) .
You made the whole title bar draggable, including the buttons. That means that the role of the buttons is also a draggable area, so when you click them, you start the drag operation instead of sending a click event. The solution is therefore to make sure the button area does not have the -webkit-app-region: drag style but only the area left to them has. This will probably require you to redesign the HTML layout for the title bar a bit, since this won't work well with the whole thing being a grid.
For more details, see this tutorial.

Related

In extension the callbacks are accumulating and persisting until browser restart

I am porting a chess game parser to new manifest v3 specification. When clicking context menu on a selected text, it plays visually the selected text as PGN chess game notation.
The problem is that background script on each game execution adds one more chrome.runtime.onMessage listener, and all the listeners persists until browser closed. So, all listeners are invoked and only first game is played each time.
There is the code:
//background.js
function playBoard (info, imgPath, windowAttributes)
{
console.log("play board with: " + info.selectionText);
let gameObject = {
chessObject:
{
gametype : "PGN_OR_FEN_board",
content : info.selectionText,
imgPath : imgPath
}
};
let windowPromise = chrome.windows.create(windowAttributes);
windowPromise.then((value) => {
chrome.runtime.onMessage.addListener(
(request, sender, sendResponse) =>
{
console.log("backg: chrome.runtime.onMessage()> " + gameObject.chessObject.content);
sendResponse(gameObject);
}
);
});
}
The above code is invoked in following context:
//background.js
console.log("enter global>");
var idParser = "parser parent";
var idMiniBoard = "board mini";
var idMediuBoard = "board medium";
chrome.runtime.onInstalled.addListener(
(details)=>
{
console.log("on installing");
idParser = chrome.contextMenus.create({"id":idParser, "title": "Chess Parser", "contexts":["selection"]});
idMiniBoard = chrome.contextMenus.create({"id":idMiniBoard, "title": "Play Small", "contexts":["selection"], "parentId": idRoatta});
idMediuBoard = chrome.contextMenus.create({"id":idMediuBoard, "title": "Play Medium", "contexts":["selection"], "parentId": idRoatta});
chrome.contextMenus.onClicked.addListener(menuItemClick);
console.log("on installed");
}
)
function menuItemClick(clickData, tab)
{
switch (clickData.menuItemId)
{
case idMiniBoard:
playBoard (clickData, "mini18", {url: "board.html", type:"popup", height : 350, width : 350});
return;
case idMediuBoard:
playBoard (clickData, "medium35", {url: "board.html", type:"popup", height: 450, width: 450});
return;
}
}
There is the popup window startup code
document.addEventListener('DOMContentLoaded',
(event) =>
{
chrome.runtime.sendMessage({ chessObject: { gametype : "PGN_OR_FEN_board" } },
(request) => //in fact is response, but is requested response
{
board_doc_main(request.chessObject);
});
}
);
I am trying to apply similar approach as I did for firefox, here Firefox extension, identification of context. Somehow for firefox this works. I am trying to implement like this: Popup open on click from background worker. Then popup window sends message to background worker to receive the required context, including the selection game text.
###################
UPDATE:
As suggested in response from #wOxxOm, this is exactly how I changed now
function playBoard (clickData, imgPath, windowAttributes)
{
let gameObject = {
chessObject:
{
gametype : "PGN_OR_FEN_board",
content : clickData.selectionText,
imgPath : imgPath
}
};
let windowPromise = chrome.windows.create(windowAttributes);
windowPromise.then((wnd) => {
chrome.runtime.onMessage.addListener (
function __playBoardCallback__ (request, sender, sendResponse)
{
if (sender.tab?.id === wnd.tabs[0].id)
{
chrome.runtime.onMessage.removeListener(__playBoardCallback__);
sendResponse(gameObject);
}
}
);
});
}
Unregister the listener immediately when it runs by using a named function:
async function playBoard(info, imgPath, windowAttributes) {
let gameObject = {
// .........
};
const wnd = await chrome.windows.create(windowAttributes);
const tabId = wnd.tabs[0].id;
chrome.runtime.onMessage.addListener(function _(msg, sender, sendResponse) {
if (sender.tab?.id === tabId) {
chrome.runtime.onMessage.removeListener(_);
sendResponse(gameObject);
}
});
}

Context menu not reponding for first click

I am building an extension in which content script is injected on context menu click. This functionality is working. Now the problem I am facing is that, the first click on context menu is not working.
Now the problem I am facing is that, the first click on context menu is not working. Is this a bug?.
background.js
on_message = async(message, sender, sendResponse) => {
console.log("bg.on_message");
sendResponse("from bg");
chrome.storage.local.get("list_url", function (data) {
if (typeof data.list_url != "undefined") {
urls = data.list_url
}
});
chrome.storage.local.get("list_ip", function (data) {
if (typeof data.list_ip != "undefined") {
ips = data.list_ip
}
});
chrome.storage.local.get("list_hash", function (data) {
if (typeof data.list_hash != "undefined") {
hashes = data.list_hash;
}
});
if (hashes){
hash_report = await createHashReport(hashes)
hash_table = await createHashTable(hash_report)
await chrome.storage.local.set({
"scanHash": true,
"hash_table": hash_table
}, () => {});
}
if (ips){
ip_report = await createIpReport(ips)
ip_table = await createIpTable(ip_report)
await chrome.storage.local.set({
"scanIp": true,
"ip_table": ip_table
}, () => {});
}
if (urls){
url_report = await createUrlReport(urls)
url_table = await createUrlTable(url_report)
await chrome.storage.local.set({
"scanUrl": true,
"url_table": url_table
}, () => {});
}
if ( hashes.length>0 || urls.length>0 || ips.length>0 ){
chrome.windows.create({url: "output.html", type: "popup", height:1000, width:1000});
}
}
chrome.runtime.onMessage.addListener(on_message);
genericOnClick = async () => {
// Inject the payload.js script into the current tab after the backdround has loaded
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
files: ["payload.js"]
},() => chrome.runtime.lastError);
});
}
// create context menu
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'scrapper',
"title": "Scrapper",
"contexts": ["all"]
});
});
chrome.contextMenus.onClicked.addListener(genericOnClick);
payload.js
function extract() {
htmlInnerText = document.documentElement.innerText;
url_exp = /[-a-zA-Z0-9#:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9#:%_\+.~#?&//=]*)?/gi;
regex = new RegExp(url_exp)
list_url = htmlInnerText.match(url_exp)
ip_exp = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
list_ip = htmlInnerText.match(ip_exp)
hash_exp = /\b[A-Fa-f0-9]{32}\b|\b[A-Fa-f0-9]{40}\b|\b[A-Fa-f0-9]{64}\b/g
list_hash = htmlInnerText.match(hash_exp)
await chrome.storage.local.set({ list_url: removeEmails(removeDuplicates(list_url)), list_ip: removeDuplicates(list_ip), list_hash: removeDuplicates(list_hash) });
}
chrome.runtime.sendMessage( extract());
Assuming that "removeEmails" and "removeDuplicates" are synchronous functions (only you know this), the chrome.storage.local.set method instead returns a promise. Basically you send a message to the background hoping that it can read the updated values of list_url, list_ip and list_hash, when instead it has those available before the storage.local.set (so it won't find anything on the first round).
I thought you'd already figured it out yourself with my clue.
Add await keyword before each chrome.storage.local.set

Chrome extension - button listener on notification executes multiple times

I am writing a chrome extension that makes requests to an API and I have noticed that after I create a notification from background script using chrome's notification API, the listeners on the buttons from the notification are executed multiple times. on the first run only once and then increasing. I figured that the listeners just add up on the page but I couldn't find a way to sort of refresh the background page.
This is the function that creates the notification and it's listeners.
var myNotificationID
const displayNotification=(userEmail, password, website,username) =>{
chrome.notifications.create("", {
type: "basic",
iconUrl: "./icon128.png",
title: "PERMISSION",
requireInteraction: true,
message: "question",
buttons: [{
title: "YES",
}, {
title: "NO",
}]
}, function(id) {
myNotificationID = id;
})
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
console.log('inserting')
try{
fetch (`http://localhost:8080/users/${userEmail}/accounts`,{
})
}catch(err){
}
} else if (btnIdx === 1) {
console.log('clearing')
chrome.notifications.clear(myNotificationID)
}
}
});
}
And this is where the function is called
chrome.runtime.onMessage.addListener((message, sender, response)=>{
if(message.message === 'showNotification'){
console.log('received insert')
displayNotification(message.userEmail,message.password, message.currentSite,message.username)
response({status:"received"})
}
})
the fetch within the listener is executed multiple times but the log from the onMessage listener is only displayed once, so the listener is the problem here.
I tried chrome.notifications.onButtonClicked.removeListener(), but as i mentioned there was no success.
Are there any other ways in which i could clean the listeners from the background script once they are used?
Using a notification store:
const notificationsByID = {};
chrome.notifications.onButtonClicked.addListener((notifId, btnIdx) => {
// Avoid access to the notification if not registered by displayNotification
if (!notificationsByID[ notifId ]) { return null; }
if (btnIdx === 0) {
console.log('inserting')
try{
fetch (`http://localhost:8080/users/${ notificationsByID[ notifId ].userEmail }/accounts`,{ /**/ });
}catch(err){
console.log(err);
}
delete notificationsByID[ notifId ]; // Cleanup
} else if (btnIdx === 1) {
console.log('clearing')
chrome.notifications.clear(myNotificationID);
delete notificationsByID[ notifId ]; // Cleanup
}
});
chrome.notifications.onClosed.addListener((notifId) => {
if (notificationsByID[ notifId ]) { delete notificationsByID[ notifId ]; }
});
const displayNotification=(userEmail, password, website,username) =>{
chrome.notifications.create("", {
type: "basic",
iconUrl: "./icon128.png",
title: "PERMISSION",
requireInteraction: true,
message: "question",
buttons: [{ title: "YES", }, { title: "NO", }]
}, function(id) {
// Insertion
notificationsByID[ id ] = { userEmail, password, website,username };
})
}

Component does not re-render after changes done by event listener in constructor [duplicate]

Actually this code is from ionic, and I am processing an image that I get through the photo capture. everything works fine, I get the blob, but for some reason my image is not shown in the template, I think the angular binding is not updating. What can I do to force it?
myblob:any=null;
takePhoto() {
const options: CameraOptions = {
quality: 60,
destinationType: this.camera.DestinationType.FILE_URI,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
allowEdit: true,
};
this.camera.getPicture(options).then(
(imageData) => {
this.file
.resolveLocalFilesystemUrl(imageData)
.then((entry: FileEntry) => {
entry.file((file) => {
this.readFile(file);
});
});
},
(err) => {
// Handle error
}
);
}
readFile(file: any) {
const reader = new FileReader();
reader.onloadend = () => {
const imgBlob = new Blob([reader.result], {
type: file.type,
});
//my ngModel (updating) ****------------->
this.myblob = URL.createObjectURL(imgBlob);
console.log(this.documento_reverso);
};
reader.readAsArrayBuffer(file);
}
In my template:
<button (click)="takePhoto()">Take photo</button>
<img *ngIf="myblob" [src]="myblob"> --> is not show at first time button click
how can do it?
When I click the button and get the photo, the image is not displayed, but the second time I click the button it is immediately displayed.
You can wrap the code in NgZone.run to make sure that it runs in the Angular zone and that the change is picked up by change detection:
import { NgZone } from '#angular/core';
constructor(private ngZone: NgZone) { }
readFile(file: any) {
...
this.ngZone.run(() => {
this.myblob = URL.createObjectURL(imgBlob);
});
...
}

Prevent Jasmine Test expect() Resolving Before JS Finished Executing

I am hoping you can help. I am fairly new to Unit Testing. I have a Karma + Jasmine set up which is running a PhantomJS browser. This is all good.
What I am struggling with is I have a link on the page, when this link is clicked it injects some HTML. I want to test that the HTML has been injected.
Now at this point, I have the test working but only sometimes, from what I can figure out if my JS runs fast enough the HTML gets injected before the expect() is run. If not the test fails.
How can I make my Jasmine test wait for all JS to finish executing before the expect() is run?
The test in question is it("link can be clicked to open a modal", function() {
modal.spec.js
const modalTemplate = require('./modal.hbs');
import 'regenerator-runtime/runtime';
import 'core-js/features/array/from';
import 'core-js/features/array/for-each';
import 'core-js/features/object/assign';
import 'core-js/features/promise';
import Modal from './modal';
describe("A modal", function() {
beforeAll(function() {
const data = {"modal": {"modalLink": {"class": "", "modalId": "modal_1", "text": "Open modal"}, "modalSettings": {"id": "", "modifierClass": "", "titleId": "", "titleText": "Modal Title", "closeButton": true, "mobileDraggable": true}}};
const modal = modalTemplate(data);
document.body.insertAdjacentHTML( 'beforeend', modal );
});
it("link exists on the page", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
expect(modalLink).not.toBeNull();
});
it("is initialised", function() {
spyOn(Modal, 'init').and.callThrough();
Modal.init();
expect(Modal.init).toHaveBeenCalled();
});
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
modalLink.click();
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
afterAll(function() {
console.log(document.body);
// TODO: Remove HTML
});
});
EDIT - More Info
To further elaborate on this, The link Jasmine 2.0 how to wait real time before running an expectation put in the comments has helped me understand a bit better, I think. So what we are saying it we want to spyOn the function and wait for it to be called and then initiate a callback which then resolves the test.
Great.
My next issue is, if you look at the structure of my ModalViewModel class below, I need to be able to spyOn insertModal() to be able to do this, but the only function that is accessible in init(). What would I do to be able to move forward with this method?
import feature from 'feature-js';
import { addClass, removeClass, hasClass } from '../../01-principles/utils/classModifiers';
import makeDraggableItem from '../../01-principles/utils/makeDraggableItem';
import '../../01-principles/utils/polyfil.nodeList.forEach'; // lt IE 12
const defaultOptions = {
id: '',
modifierClass: '',
titleId: '',
titleText: 'Modal Title',
closeButton: true,
mobileDraggable: true,
};
export default class ModalViewModel {
constructor(module, settings = defaultOptions) {
this.options = Object.assign({}, defaultOptions, settings);
this.hookModalLink(module);
}
hookModalLink(module) {
module.addEventListener('click', (e) => {
e.preventDefault();
this.populateModalOptions(e);
this.createModal(this.options);
this.insertModal();
if (this.options.closeButton) {
this.hookCloseButton();
}
if (this.options.mobileDraggable && feature.touch) {
this.hookDraggableArea();
}
addClass(document.body, 'modal--active');
}, this);
}
populateModalOptions(e) {
this.options.id = e.target.getAttribute('data-modal');
this.options.titleId = `${this.options.id}_title`;
}
createModal(options) {
// Note: As of ARIA 1.1 it is no longer correct to use aria-hidden when aria-modal is used
this.modalTemplate = `<section id="${options.id}" class="modal ${options.modifierClass}" role="dialog" aria-modal="true" aria-labelledby="${options.titleId}" draggable="true">
${options.closeButton ? '<a href="#" class="modal__close icon--cross" aria-label="Close" ></a>' : ''}
${options.mobileDraggable ? '<a href="#" class="modal__mobile-draggable" ></a>' : ''}
<div class="modal__content">
<div class="row">
<div class="columns small-12">
<h2 class="modal__title" id="${options.titleId}">${options.titleText}</h2>
</div>
</div>
</div>
</section>`;
this.modal = document.createElement('div');
addClass(this.modal, 'modal__container');
this.modal.innerHTML = this.modalTemplate;
}
insertModal() {
document.body.appendChild(this.modal);
}
hookCloseButton() {
this.closeButton = this.modal.querySelector('.modal__close');
this.closeButton.addEventListener('click', (e) => {
e.preventDefault();
this.removeModal();
removeClass(document.body, 'modal--active');
});
}
hookDraggableArea() {
this.draggableSettings = {
canMoveLeft: false,
canMoveRight: false,
moveableElement: this.modal.firstChild,
};
makeDraggableItem(this.modal, this.draggableSettings, (touchDetail) => {
this.handleTouch(touchDetail);
}, this);
}
handleTouch(touchDetail) {
this.touchDetail = touchDetail;
const offset = this.touchDetail.moveableElement.offsetTop;
if (this.touchDetail.type === 'tap') {
if (hasClass(this.touchDetail.eventObject.target, 'modal__mobile-draggable')) {
if (offset === this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = '0px';
} else {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
}
} else if (offset > this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
} else {
this.touchDetail.eventObject.target.click();
}
} else if (this.touchDetail.type === 'flick' || (this.touchDetail.type === 'drag' && this.touchDetail.distY > 200)) {
if (this.touchDetail.direction === 'up') {
if (offset < this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = '0px';
} else if (offset > this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
}
} else if (this.touchDetail.direction === 'down') {
if (offset < this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
} else if (offset > this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = '95%';
}
}
} else {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.moveableElementStartY}px`;
}
}
removeModal() {
document.body.removeChild(this.modal);
}
static init() {
const instances = document.querySelectorAll('[data-module="modal"]');
instances.forEach((module) => {
const settings = JSON.parse(module.getAttribute('data-modal-settings')) || {};
new ModalViewModel(module, settings);
});
}
}
UPDATE
After working through it has been discovered that .click() events are asynchronous which is why I am gettnig the race issue. Documentation & Stack Overflow issues thoughtout the web recommend using createEvent() and dispatchEvent() as PhantomJs does not understand new MouseEvent().
Here is my code which is now trying to do this.
modal.spec.js
// All my imports and other stuff
// ...
function click(element){
var event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
element.dispatchEvent(event);
}
describe("A modal", function() {
// Some other tests
// Some other tests
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
click(modalLink);
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
// After all code
// ...
});
Unfortunately this is producting the same results. 1 step closer but not quite there.
After a touch of research, it looks as though your use of the click event is triggering an asynchronous event loop essentially saying "Hey set this thing to be clicked and then fire all the handlers"
Your current code can't see that and has no real way of waiting for it. I do believe you should be able to build and dispatch a mouse click event using the info here.
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent
I think that should allow you to build a click event and dispatch it onto your element. The difference is that dispatchEvent is synchronous - it should block your test until the click handlers have completed. That should allow you to do your assertion without failures or race conditions.
I have finally found a solution.
There are 2 parts to this, the first part came from #CodyKnapp. His insight into a click() function running asynchronously helped to solve the first part of the issue.
Here is the code for this part.
modal.spec.js
// All my imports and other stuff
// ...
function click(element){
var event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
element.dispatchEvent(event);
}
describe("A modal", function() {
// Some other tests
// Some other tests
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
click(modalLink);
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
// After all code
// ...
});
This allowed for the code to run synchronously.
The second part was a poor understanding on my part of how to write Jasmine tests. In my original tests I was running Modal.init() inside of it("is initialised", function() { when actually I want to be running this inside of beforeAll(). This fixed the issue I had where my tests would not always be successful.
Here is my final code:
modal.spec.js
const modalTemplate = require('./modal.hbs');
import '#babel/polyfill';
import Modal from './modal';
function click(element){
var event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
element.dispatchEvent(event);
}
describe("A modal", function() {
beforeAll(function() {
const data = {"modal": {"modalLink": {"class": "", "modalId": "modal_1", "text": "Open modal"}, "modalSettings": {"id": "", "modifierClass": "", "titleId": "", "titleText": "Modal Title", "closeButton": true, "mobileDraggable": true}}};
const modal = modalTemplate(data);
document.body.insertAdjacentHTML( 'beforeend', modal );
spyOn(Modal, 'init').and.callThrough();
Modal.init();
});
it("link exists on the page", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
expect(modalLink).not.toBeNull();
});
it("is initialised", function() {
expect(Modal.init).toHaveBeenCalled();
});
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
click(modalLink);
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
afterAll(function() {
console.log(document.body);
// TODO: Remove HTML
});
});

Categories

Resources