I write my first *.gs file of Google Ads-script.
Is there any IDE or environment where I can add breakpoints or see the variables state?
I saw only logger printing, but that's not efficient to work with.
I have tried #Andrew's reply, but didn't manage:
You can place dots in next to the line numbers and then click on the little bug icon, like displayed on this image.
This will open this debug screen:
you can use this function.
For example:
MyLogger("test");
function MyLogger(s,d=true,w=800,h=400,t=5) {
const cs=CacheService.getScriptCache();
const cached=cs.get("Logger");
const ts=Utilities.formatDate(new Date(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "MM|dd|HH:mm:ss")
if(cached) {
var v=Utilities.formatString('%s<br />[%s] - %s',cached,ts,s);
}else{
var v=Utilities.formatString('[%s] - %s',ts,s);
}
cs.put("Logger",v,t);
//allows logging without displaying.
if(d) {
const a='<br /><input type="button" value="Exit" onClick="google.script.host.close();" />';
const b='<br /><input type="button" value="Exit" onClick="google.script.host.close();" /><br />';
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(b+v+a).setWidth(w).setHeight(h), 'My Logger');
}
}
(To view logs open spreadsheet with your script)
Related
I have tried many options, but not successful so far to click on checkbox that are custom checkboxes with :before tag and are hidden. Can someone show me the way to resolve this issue. I tried X-Path and other selector, it finds and clicks on those checkboxes but those checkboxes don't get checked for some reason.
<fieldset class="checkbox">
<legend>Services</legend>
<ul class="multiColumnList">
<li><label for="AccountUsers_0__ViewOrders"><input data-val="true" data-val-required="The View Orders field is required." id="AccountUsers_0__ViewOrders" name="AccountUsers[0].ViewOrders" type="checkbox" value="true" class="hidden-field"><span class="custom checkbox"></span><input name="AccountUsers[0].ViewOrders" type="hidden" value="false">View Orders</label></li>
Here is the screenshot of HTML
Try to click on the checkbox in the following way:
const checkboxSelector = Selector('#AccountUsers_0__ViewOrders');
const labelSelector = Selector('[for="AccountUsers_0__ViewOrders"]')
await t.click(labelSelector);
await t.expect(checkboxSelector.checked).ok();
If this does not help, let me know. I will find a suitable solution for you.
async Check() {
const checkboxSelector = Selector(`[id="AccountUsers_0__ViewOrders"]`)
.with({visibilityCheck: true});
if(!checkboxSelector.checked){
await t.click(checkboxSelector);
}
await t.expect(checkboxSelector.checked).eql(true, 'Should be checked')
}
async UnCheck() {
const checkboxSelector = Selector(`[id="AccountUsers_0__ViewOrders"]`)
.with({visibilityCheck: true});
if(checkboxSelector.checked){
await t.click(checkboxSelector);
}
await t.expect(checkboxSelector.checked).eql(false, 'Should be unchecked')
}
Please try this code and let me know
Hello I have a simple page of our application, the start page that has a disclaimer on it and one button. Because of the technology we are using the IDs are stripped out and obfuscated.
So the button's name is 'ok' as it appears in the page-source view below. Note no ID. Even if I put it in our code, when it is produced the ID is removed.
<div style="text-align:center;">
<input type="button" name="ok" value="Ok" width="40" onclick="swap()" />
</div>
I have the following in my TestCafe js file:
import {Selector } from 'testcafe';
import { ClientFunction } from 'testcafe';
fixture `My fixture`
.page `http://192.168.2.86/mpes/login.jsp`;
test('Disclaimer Page 1', async t => {
const okBtn = Selector('button').withAttribute('ok');
await t
.click(okBtn);
});
test('Disclaimer Page 2 -- of course tried this ', async t => {
await t
.click('ok');
});
.click('ok') -- does not work either
Tried numerous things even trying findElementByName .. but nothing works with Window vs Document and Selector vs element.
Any help would be greatly appreciated.
I have been all over looking at how to grab selectors on TestCafe docs. None seem to fit this scenario which is quite simple -- but highly problematic.
Thanks in advance.
If you want to select this element:
<input type="button" name="ok" value="Ok" width="40" onclick="swap()" />
you can use (one or more) attribute selectors like this:
input[type="button"][name="ok"]
If only one element on the page has the attribute name="ok", you can grab that single element in javascript, using document.querySelector(), like this:
const myButton = document.querySelector('input[type="button"][name="ok"]');
N.B. If multiple elements on the page have the attribute name="ok", you can grab all of them in javascript, using document.querySelectorALL().
I got a component that let's the user upload a profile picture with a preview before sending it off to cloudinary.
<template>
<div>
<div
class="image-input"
:style="{ 'background-image': `url(${person.personData.imagePreview})` } "
#click="chooseImage"
>
<span
v-if="!person.personData.imagePreview"
class="placeholder"
>
<i class="el-icon-plus avatar-uploader-icon"></i>
</span>
<input
type="file"
ref="fileInput"
#change="previewImage"
>
</div>
</div>
</template>
The methods to handle the preview:
chooseImage() {
this.$refs.fileInput.click()
},
previewImage(event) {
// Reference to the DOM input element
const input = event.target;
const files = input.files
console.log("File: ", input.files)
if (input.files && input.files[0]) {
this.person.personData.image = files[0];
const reader = new FileReader();
reader.onload = (e) => {
this.person.personData.imagePreview = e.target.result;
}
// Start the reader job - read file as a data url (base64 format)
reader.readAsDataURL(input.files[0]);
}
},
This works fine, except for when the user fetches a previous project from the DB. this.person.personData.imagePreview get's set to something like https://res.cloudinary.com/resumecloud/image/upload/.....id.jpg Then when the user wants to change his profile picture, he is able to select a new one from his local file system, and this.person.personData.imagePreview is read again as a data url with base64 format. But the preview doesn't work. Only when I change routes back and forth, the correct image selected by the user is displayed.
Like I said in the comment on my post. Turns out I'm an idiot. When displaying the preview, I used this.person.personData.imagePreview . When a user fetches a project from the DB, I just did this.person.personData = response.data. That works fine, apart from the fact that I had a different name for imagePreview on my backend. So I manually set it on the same load method when fetching from the DB like: this.person.personData.imagePreview = this.loadedPersonData.file. For some reason, that screwed with the reactivity of Vue.
I have set up the aurelia-dialog plugin. It's working using the example in the GitHub readme, but the documentation doesn't explain anything about how to use it otherwise. I have a simple use case with a list page. I want to click an "add new" button, pop the modal dialog which has it's own VM. The modal contains a simple dropdown list. I need to select an item on the list and make an API call to save the data, but I can't seem to figure out how to wire up my save method with the save button on the dialog.
The method that opens the dialog on my list page (which works just fine):
loadAgencyDialog(id){
this.dialogService.open({ viewModel: AddAgency, model: { id: id }}).then((result) => {
if (!result.wasCancelled) {
console.log('good');
console.log(result.output);
} else {
console.log('bad');
}
});
My modal add-agency.js (VM for the modal, also loads the select list just fine and yes, I have a variable named kase because case is reserved):
import {DialogController} from 'aurelia-dialog';
import {ApiClient} from 'lib/api-client';
import {inject} from 'aurelia-framework';
#inject(DialogController, apiClient)
export class AddAgency {
kase = { id: '' };
constructor(controller, apiClient){
this.controller = controller;
this.agencies = [];
this.apiClient = apiClient;
}
activate(kase){
this.kase = kase;
this.apiClient.get('agencies')
.then(response => response.json())
.then(agencies => this.agencies = agencies.data)
.then(() => console.log(this.agencies)); //these load fine
}
addAgency() {
//Do API call to save the agency here, but how?
}
}
This is part I'm unsure about. In the example, they use controller.ok(theobjectpassedin), which returns a promise. But I don't get where I can call my addAgency method. Any ideas?
It's possible I'm misunderstanding your question, but you should be able to just call addAgency() in your HTML:
<button click.trigger="addAgency()">Add</button>
And then do what you need to do in addAgency(), finishing with a call to this.controller.ok() to wrap up the modal.
As an example, here's my modal's dialog-footer:
<ai-dialog-footer>
<button click.trigger="controller.cancel()">Cancel</button>
<button click.trigger="ok(item)">Save</button>
</ai-dialog-footer>
And in my code:
ok(item) {
this.controller.ok(item);
}
Not too complex. Hope that helps.
I wrote a script for uploading files to my Google Drive using the Google Script. I deployed it as a WebApp but it's not working and I don't know why. The button just gets stuck on "Subiendo..." and never changes anything inside my Drive. I'm sure I gave the script all the permissions and I already tried to see what's happening on my own without any sucess. Can you maybe help me find what should I do? I post the code here:
Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('main').setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function serverFunc(theForm) {
try{
var folder_name = "publicaciones";
var folder,folders = DriveApp.getFoldersByName(folder_name);
if(folders.hasNext()){
folder = folders.next();
}else{
folder = DriveApp.createFolder(folder_name);
}
Logger.log("TheForm", theForm == null);
var blob = theForm.theFile;
Logger.log("Blob!", blob == null);
var file = folder.createFile(blob);
Logger.log("File:", file.getName());
return "Archivo subido exitosamente: " + file.getUrl();
} catch(error){
Logger.log("error: ", error.toString());
return error.toString();
}
}
** main.html **
<div>
<form id="myForm">
<input type="file" name="theFile">
<input type="hidden" name="anExample">
<br>
<input type="button" value="Subir Archivo" onclick="this.value='Subiendo...';
google.script.run.withSuccessHandler(fileUploaded).serverFunc(this.parentNode);
return false;">
</form>
</div>
<div id="output">
</div>
<script>
function fileUploaded(status) {
document.getElementById('myForm').style.display = 'none';
document.getElementById('output').innerHTML = status;
}
</script>
I'd appreaciate any help or pointers you can give me. Thanks a lot!
Looks like there is a bug currently that Google is working on. I found a possible fix:
Change your input tag to this:
<input type="button" value="Subir Archivo" onclick="callServerCode()"/>
Add a function to the <script> tag:
<script>
function callServerCode() {
var formToSend = document.getElementById('myForm');
google.script.run
.withSuccessHandler(fileUploaded)
.serverFunc(formToSend);
};
function fileUploaded(status) {
document.getElementById('myForm').style.display = 'none';
document.getElementById('output').innerHTML = status;
}
</script>
Note that inside the new function, it gets the form using var formToSend = document.getElementById('myForm');
And change IFRAME to NATIVE.
It's a doc issue needs to fix with Google when using SandBoxMode=IFRAME currently. See here. I've tested it works by swapping IFRAME to NATIVE. You can simply do it:
function doGet() {
return HtmlService.createHtmlOutputFromFile('main');
}
Google has turned of NATIVE for SandBoxMode. It is now set to IFRAME only.
See here