Office JavaScript API: selecting a range in Word for Mac - ms-word

I'm working on a side project using the Microsoft Office JavaScript APIs. I have some functionality working to select a range in order to scroll to a particular position within a document. This works as expected in Office for the web, but in Office for Mac I get the following error when calling context.sync().then():
Unhandled Promise Rejection: RichApi.Error: ItemNotFound
I can't find any documentation on that particular error, and I'm not sure how to troubleshoot what I might be doing wrong. What am I missing? Like I said, this works in the web interface.
Here is minimal sample of code that demonstrates the problem:
function UI(context) {
this.context = context;
}
UI.prototype.initialize = function() {
var paragraphs = this.context.document.body.paragraphs;
this.context.load(paragraphs);
document.querySelector('button').addEventListener('click', () => {
this.context.sync().then(() => {
this.goToRange(paragraphs.items[0]);
});
});
};
UI.prototype.goToRange = function(range) {
range.select();
this.context.sync();
};
document.addEventListener('DOMContentLoaded', () => {
Office.onReady(() => {
Word.run(context => {
return context.sync().then(() => {
new UI(context).initialize();
});
});
});
});
The only thing I can think of is that maybe the reference to the paragraph client object becomes "stale" in some sense, perhaps based on some resource limits that are lower in the Mac application than in the online interface? (That would be counterintuitive to me, but it's the only thing that comes to mind.)

I think I figured out the problem. I stumbled upon a hint while putting together the minimum code sample in the question; I removed a little too much code at first and encountered the following error:
Unhandled Promise Rejection: RichApi.Error: The batch function passed
to the ".run" method didn't return a promise. The function must return
a promise, so that any automatically-tracked objects can be released
at the completion of the batch operation.
I believe the issue is that, at least in Word for Mac, you can't use the context object provided by Word.run in an asynchronous event listener. I'm guessing this is because, as the above error states, some state has been released after resolving the promise returned. I can get the code to work by adding a dedicated call to Word.run (and using the fresh context provided) inside the event listener.
It is still a little odd that it works just fine in the browser. Presumably, the same state is not released as aggressively in the browser-based version.

Related

Stop huge error output from testing-library

I love testing-library, have used it a lot in a React project, and I'm trying to use it in an Angular project now - but I've always struggled with the enormous error output, including the HTML text of the render. Not only is this not usually helpful (I couldn't find an element, here's the HTML where it isn't); but it gets truncated, often before the interesting line if you're running in debug mode.
I simply added it as a library alongside the standard Angular Karma+Jasmine setup.
I'm sure you could say the components I'm testing are too large if the HTML output causes my console window to spool for ages, but I have a lot of integration tests in Protractor, and they are SO SLOW :(.
I would say the best solution would be to use the configure method and pass a custom function for getElementError which does what you want.
You can read about configuration here: https://testing-library.com/docs/dom-testing-library/api-configuration
An example of this might look like:
configure({
getElementError: (message: string, container) => {
const error = new Error(message);
error.name = 'TestingLibraryElementError';
error.stack = null;
return error;
},
});
You can then put this in any single test file or use Jest's setupFiles or setupFilesAfterEnv config options to have it run globally.
I am assuming you running jest with rtl in your project.
I personally wouldn't turn it off as it's there to help us, but everyone has a way so if you have your reasons, then fair enough.
1. If you want to disable errors for a specific test, you can mock the console.error.
it('disable error example', () => {
const errorObject = console.error; //store the state of the object
console.error = jest.fn(); // mock the object
// code
//assertion (expect)
console.error = errorObject; // assign it back so you can use it in the next test
});
2. If you want to silence it for all the test, you could use the jest --silent CLI option. Check the docs
The above might even disable the DOM printing that is done by rtl, I am not sure as I haven't tried this, but if you look at the docs I linked, it says
"Prevent tests from printing messages through the console."
Now you almost certainly have everything disabled except the DOM recommendations if the above doesn't work. On that case you might look into react-testing-library's source code and find out what is used for those print statements. Is it a console.log? is it a console.warn? When you got that, just mock it out like option 1 above.
UPDATE
After some digging, I found out that all testing-library DOM printing is built on prettyDOM();
While prettyDOM() can't be disabled you can limit the number of lines to 0, and that would just give you the error message and three dots ... below the message.
Here is an example printout, I messed around with:
TestingLibraryElementError: Unable to find an element with the text: Hello ther. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
...
All you need to do is to pass in an environment variable before executing your test suite, so for example with an npm script it would look like:
DEBUG_PRINT_LIMIT=0 npm run test
Here is the doc
UPDATE 2:
As per the OP's FR on github this can also be achieved without injecting in a global variable to limit the PrettyDOM line output (in case if it's used elsewhere). The getElementError config option need to be changed:
dom-testing-library/src/config.js
// called when getBy* queries fail. (message, container) => Error
getElementError(message, container) {
const error = new Error(
[message, prettyDOM(container)].filter(Boolean).join('\n\n'),
)
error.name = 'TestingLibraryElementError'
return error
},
The callstack can also be removed
You can change how the message is built by setting the DOM testing library message building function with config. In my Angular project I added this to test.js:
configure({
getElementError: (message: string, container) => {
const error = new Error(message);
error.name = 'TestingLibraryElementError';
error.stack = null;
return error;
},
});
This was answered here: https://github.com/testing-library/dom-testing-library/issues/773 by https://github.com/wyze.

Protractor element.click() throwing an exception

I was trying to figure out why .click() below was crashing protractor :
this.clickSecondPanel = function () {
element(by.css('div.panels-gs.panel-top-two-gs')).click();
}
until I changed the line to :
element(by.css('div.panels-gs.panel-top-two-gs')).click;
where my spec.js looks something like :
var DataCardPage = require('./pageObjects/dataCard.page.js');
var dataCardPage = new DataCardPage();
describe('Clicking on the 2nd panel', function () {
dataCardPage.clickSecondPanel();
it('Should select the 2nd test panel', function () {
expect(dataCardPage.getSecondPanelText()).toBe('TEST123');
});
In other places in my code, I use .click() (with parenths), so this is confusing to me.
The error is nasty:
Started
[17:44:23] E/launcher - Error while waiting for Protractor to sync with the page
: "window.angular is undefined. This could be either because this is a non-angu
lar page or because your test involves client-side navigation, which can interfe
re with Protractor's bootstrapping. See http://git.io/v4gXM for details"
Any advice appreciated...
Bob
Solved this in the comments above, posting as an answer.
My suggestion was to try moving the clickSecondPanel() inside the it block. It looked suspicious by itself just from a "best practice" perspective as I do not have any code that is outside of a jasmine function i.e. it, beforeAll, afterAll etc (don't even know where I learned that habit honestly).
It also seemed to effect the control flow and asynchronous execution so the click() event was triggering too soon. This can be explained in part by this documentation and/or this blog post
Try using browser.ignoreSynchronization=true at the begining of your test. May be the application that you are trying to automated does not contain angular in it.

Add single record to mongo collection with meteor

I am a new user to JavaScript and the meteor framework trying to understand the basic concepts. First of all I want to add a single document to a collection without duplicate entries.
this.addRole = function(roleName){
console.log(MongoRoles.find({name: roleName}).count());
if(!MongoRoles.find({name: roleName}).count())
MongoRoles.insert({name: roleName});
}
This code is called on the server as well as on the client. The log message on the client tells me there are no entries in the collection. Even if I refresh the page several times.
On the server duplicate entries get entered into the collection. I don't know why. Probably I did not understand the key concept. Could someone point it out to me, please?
Edit-1:
No, autopublish and insecure are not installed anymore. But I already published the MongoRoles collection (server side) and subscribed to it (client side). Furthermore I created a allow rule for inserts (client side).
Edit-2:
Thanks a lot for showing me the meteor method way but I want to get the point doing it without server side only methods involved. Let us say for academic purposes. ;-)
Just wrote a small example:
Client:
Posts = new Mongo.Collection("posts");
Posts.insert({title: "title-1"});
console.log(Posts.find().count());
Server:
Posts = new Mongo.Collection("posts");
Meteor.publish(null, function () {
return Posts.find()
})
Posts.allow({
insert: function(){return true}
})
If I check the server database via 'meteor mongo' it tells me every insert of my client code is saved there.
The log on the client tells me '1 count' every time I refresh the page. But I expected both the same. What am I doing wrong?
Edit-3:
I am back on my original role example (sorry for that). Just thought I got the point but I am still clueless. If I check the variable 'roleCount', 0 is responded all the time. How can I load the correct value into my variable? What is the best way to check if a document exists before the insertion into a collection? Guess the .find() is asynchronous as well? If so, how can I do it synchronous? If I got it right I have to wait for the value (synchronous) because I really relay on it.
Shared environment (client and server):
Roles = new Mongo.Collection("jaqua_roles");
Roles.allow({
insert: function(){return true}
})
var Role = function(){
this.addRole = function(roleName){
var roleCount = Roles.find({name: roleName}).count();
console.log(roleCount);
if(roleCount === 0){
Roles.insert({name: roleName}, function(error, result){
try{
console.log("Success: " + result);
var roleCount = Roles.find({name: roleName}).count();
console.log(roleCount);
} catch(error){
}
});
}
};
this.deleteRole = function(){
};
}
role = new Role();
role.addRole('test-role');
Server only:
Meteor.publish(null, function () {
return Roles.find()
})
Meteor's insert/update/remove methods (client-side) are not a great idea to use. Too many potential security pitfalls, and it takes a lot of thought and time to really patch up any holes. Further reading here.
I'm also wondering where you're calling addRole from. Assuming it's being triggered from client-side only, I would do this:
Client-side Code:
this.addRole = function(roleName){
var roleCount = MongoRoles.find({name: roleName}).count();
console.log(roleCount);
if (roleCount === 0) {
Meteor.call('insertRole', roleName, function (error, result) {
if (error) {
// check error.error and error.reason (if I'm remembering right)
} else {
// Success!
}
});
}
}
How I've modified this code and why:
I made a roleCount variable so that you can avoid calling MongoRoles.find() twice like that, which is inefficient and consumes unneeded resources (CPU, disk I/O, etc). Store it once, then reference the variable instead, much better.
When checking numbers, try to avoid doing things like if (!count). Using if (count === 0) is clearer, and shows that you're referencing a number. Statements like if (!xyz) would make one think this is a boolean (true/false) value.
Always use === in JavaScript, unless you want to intentionally do a loose equality operation. Read more on this.
Always use open/closed curly braces for if and other blocks, even if it contains just a single line of code. This is just good practice so that if you decide to add another line later, you don't have to then wrap it in braces. Just a good practice thing.
Changed your database insert into a Meteor method (see below).
Side note: I've used JavaScript (ES5), but since you're new to JavaScript, I think you should jump right into ES6. ES is short for ECMAScript (which is what JS is based on). ES6 (or ECMAScript 2015) is the most recent stable version which includes all kinds of new awesomeness that JavaScript didn't previously have.
Server-side Code:
Meteor.method('insertRole', function (roleName) {
check(roleName, String);
try {
// Any security checks, such as logged-in user, validating roleName, etc
MongoRoles.insert({name: roleName});
} catch (error) {
// error handling. just throw an error from here and handle it on client
if (badThing) {
throw new Meteor.Error('bad-thing', 'A bad thing happened.');
}
}
});
Hope this helps. This is all off the top of my head with no testing at all. But it should give you a better idea of an improved structure when it comes to database operations.
Addressing your edits
Your code looks good, except a couple issues:
You're defining Posts twice, don't do that. Make a file, for example, /lib/collections/posts.js and put the declaration and instantiation of Mongo.Collection in there. Then it will be executed on both client and server.
Your console.log would probably return an error, or zero, because Posts.insert is asynchronous on the client side. Try the below instead:
.
Posts.insert({title: "title-1"}, function (error, result) {
console.log(Posts.find().count());
});

Why is ic-ajax not defined within certain functions in Ember CLI?

Forgive my ignorance, but I can't get ic-ajax working inside of certain
functions.
Specifically, I'd like to get a test like this working, but for Ember CLI:
e.g. http://coderberry.herokuapp.com/testing-your-ember-application#30
I can call ajax inside Ember.Object.Extend and outside of functions and object definitions, but not in modules, tests, or Ember.Route's model function.
Am I misunderstanding something or is there a misconfiguration in my app?
I've figured out that within functions I can do:
ajax = require('ic-ajax')['default'];
defineFixture = require('ic-ajax')['defineFixture'];
but I'm pretty sure import at the top of the file is supposed to work.
I'm experiencing this on Ember 0.40.0 (both in my existing app and a fresh app). See below for more specifics where I'm finding it undefined. Setting var ajax = icAjaxRaw outside of the functions does not work either. I'm at a bit of a loose end so any help you could give in this regard would be great.
users-test.js:
import ajax from 'ic-ajax';
import { raw as icAjaxRaw } from 'ic-ajax';
import { defineFixture as icAjaxDefineFixture } from 'ic-ajax';
debugger;
---> icAjaxDefineFixture IS defined here
module('Users', {
setup: function() {
App = startApp();
debugger;
icAjaxDefineFixture --> UNDEFINED
},
teardown: function() {
Ember.run(App, App.destroy);
}
});
test("Sign in", function() {
icAjaxDefineFixture --> UNDEFINED
expect(1);
visit('/users/sign-in').then(function() {
equal(find('form').length, 1, "Sign in page contains a form");
});
});
Brocfile.js (I don't think these are actually needed with the new ember-cli-ic-ajax addon):
app.import('vendor/ic-ajax/dist/named-amd/main.js', {
exports: {
'ic-ajax': [
'default',
'defineFixture',
'lookupFixture',
'raw',
'request',
]
}
});
Had the same problem. Turns out it is a Chrome debugger optimization issue, checkout this blog post http://johnkpaul.com/blog/2013/04/03/javascript-debugger-surprises/
While debugging, if you try to use a variable from a closure scope in the console, that wasn’t actually used in the source, you’ll be surprised by ReferenceErrors. This is because JavaScript debuggers optimize the hell out of your code and will remove variables from the Lexical Environment of a function if they are unused.
To play around in debugger, I've just typed ajax; inside of the closure and variable magically appeared.

How do I customize wintersmith paginator?

I've been setting up a site with Wintersmith and am loving it for the most part, but I cannot wrap my head around some of the under-the-hood mechanics. I started with the "blog" skeleton that adds the paginator.coffee plugin.
The question requires some details, so up top, what I'm trying to accomplish:
Any files (markdown, html, json metadata) will be picked up either in /contents/article/<file> or /contents/articles/<subdir>/<file>
Output files are at /articles/YYYY/MM/DD/title-slug/
/blog.html lists all articles, paginated.
Files just under /contents (not in articles) are not treated as blog posts. Markdown and JSON metadata are still processed, but no permalinked URLs, not included in blog listings, file/directory structure is more directly copied over.
So, I solved #1 with this suggestion: How can I have articles in Wintersmith not in their own subdirectory? So far, great, and #3 is working -- the paginated listing includes all posts. #4 has not been an issue, it's the default behavior.
On #2 I found this solution: http://andrewphilipclark.com/2013/11/08/removing-the-boilerplate-from-wintersmith-blog-posts/ . As the author mentions, his solution was (sort of) subsequently incorporated into Wintersmith master, so I tried just setting the filenameTemplate accordingly. Unfortunately this applies to all content, not just that under /articles, so the rest of my site gets hosed (breaks #4). So then I tried the author's approach, adding a blogpost.coffee plugin using his code. This generates all the files out of /contents/articles into the correct permalink URLs, however the paginator now for some reason will no longer see files directly under /articles (point #1).
I've tried a lot of permutations and hacking. Tried changing the order of which plugin gets loaded first. Tried having PaginatorPage extend BlogpostPage instead of Page. Tried a lot of things. I finally realize, even after inspecting many of the core classes in Wintersmith source, that I do not understand what is happening.
Specifically, I cannot figure out how contents['articles']._.pages and .directories are set, which seems relevant. Nor do I understand what that underscore is.
Ultimately, Jade/CoffeeScript/Markdown are a great combo for minimizing coding and enhancing clarity except when you want to understand what's happening under the hood and you don't know these languages. It took me a bit to get the basics of Jade and CoffeeScript (Markdown is trivial of course) enough to follow what's happening. When I've had to dig into the wintersmith source, it gets deeper. I confess I'm also a node.js newbie, but I think the big issue here is just a magic framework. It would be helpful, for instance, if some of the core "plugins" were included in the skeleton site as opposed to buried in node_modules, just so curious hackers could see more quickly how things interconnect. More verbose docs would of course be helpful too. It's one thing to understand conceptually content trees, generators, views, templates, etc., but understanding the code flow and relations at runtime? I'm lost.
Any help is appreciated. As I said, I'm loving Wintersmith, just wish I could dispel magic.
Because coffee script is rubbish, this is extremely hard to do. However, if you want to, you can destroy the paginator.coffee and replace it with a simple javascript script that does a similar thing:
module.exports = function (env, callback) {
function Page() {
var rtn = new env.plugins.Page();
rtn.getFilename = function() {
return 'index.html';
},
rtn.getView = function() {
return function(env, locals, contents, templates, callback) {
var error = null;
var context = {};
env.utils.extend(context, locals);
var buffer = new Buffer(templates['index.jade'].fn(context));
callback(error, buffer);
};
};
return rtn;
};
/** Generates a custom index page */
function gen(contents, callback) {
var p = Page();
var pages = {'index.page': p};
var error = null;
callback(error, pages);
};
env.registerGenerator('magic', gen);
callback();
};
Notice that due to 'coffee script magic', there are a number of hoops to jump through here, such as making sure you return a buffer from getView(), and 'manually' overriding rather than using the obscure coffee script extension semantics.
Wintersmith is extremely picky about how it handles these functions. If callbacks are not invoked, for the returned value is not a Stream or Buffer, generated files will appear in the content summary, but not be rendered to disk during a build. Enable verbose logging and check of 'skipping foo' messages to detect this.