Why submitHandler is not working on mobile? - microsoft-teams

We've implemented this code in the tab on our app:
microsoftTeams.tasks.startTask(
taskInfo: TaskInfo,
submitHandler?: (err: string, result: string | any) => void
): void;
microsoftTeams.tasks.submitTask(
result?: string | any,
appIds?: string | string[]
): void;
It works fine on the web app, but it doesn't work on the mobile app, the submitHandler callback is never called.
Please advice.
Thank you,

Related

Running some code only if it's tested by cypress

I'm sure I'm missing something very obvious here, but I need to put an if statement in my application code that takes one branch if the current request is from cypress, and the other if not.
(off-topic: I know full well that usually this is a Very-Bad-Idea®, because you want to test the app exactly as it is seen by end users. On the other hand, we live in the real world, with limited time, and sometimes a small bad idea is allowed)
One way seems to be to manually change the user agent or add headers to the cypress calls, I just want to know if there is a very obvious (and better) way of doing this.
When you run the app from a Cypress test, the window object has a property called Cypress.
So inside the app you can test for this property
if (window.Cypress) {
// testing is active
...
} else {
// testing not active
...
}
You can set a flag to indicate you are running the application through cypress.
for example, using the session storage:
Cypress.Commands.add('setFlag', (flag: string, value: string | boolean) => {
cy.window().then((w) => {
w.sessionStorage.setItem('flags.' + flag, value.toString());
});
});
//in the test
before(() => {
cy.setFlag("test_mode", true)
})
And in your app
if(window.sessionStorage.getItem("flags.test_mode"))
//do stuff
else
//do other stuff
You could also implement some sort of service that will manage it.
export class FlagsService {
public get isNotProd() {
return window.location.origin !== "ProdDomain";
}
get<T>(flag: string): T | null {
if (this.isNotProd) {
const key = 'flags.' + flag;
const rawFlag = window.sessionStorage.getItem(key);
if (rawFlag) {
return rawFlag as unknown as T;
}
}
return null;
}

How can I detect when an Electron window is being copied from? Having trouble intercepting the WM_CLIPBOARDUPDATE message

I want to detect when the user has copied from a window.
To intercept the WM_CLIPBOARDUPDATE message, I have to first register the window to receive the message via the AddClipboardFormatListener.
The docs say:
When a window has been added to the clipboard format listener list, it is posted a WM_CLIPBOARDUPDATE message whenever the contents of the clipboard have changed.
My Electron app is doing just this.
main.js:
const {app, BrowserWindow} = require('electron')
app.whenReady().then(async () => {
const mainWindow = new BrowserWindow();
await mainWindow.loadFile('index.html')
mainWindow.webContents.on("did-create-window", (window, details) => {
const handle = Array.from(window.getNativeWindowHandle());
// await sending handle to a .NET process to register the window with the clipboard thing as per the Remarks section in the docs below
const WM_CLIPBOARDUPDATE= 0x031D; // https://learn.microsoft.com/en-us/windows/win32/dataxchg/wm-clipboardupdate
window.hookWindowMessage(WM_CLIPBOARDUPDATE, (wp, lp) =>
{
console.log(wp);
});
});
})
app.on('window-all-closed', function () {
app.quit();
})
index.html
<button onclick="window.open('https://google.com')">Open Window</button>
And my .NET process is doing this:
using System.Runtime.InteropServices;
public static void RegisterWindowWithClipboard(byte[] hwndBuffer) {
if (!BitConverter.IsLittleEndian) {
Array.Reverse(hwndBuffer);
}
IntPtr winHandle;
if (hwndBuffer.Length == 4) winHandle = (IntPtr)BitConverter.ToUInt32(hwndBuffer, 0);
else winHandle = (IntPtr)BitConverter.ToUInt64(hwndBuffer, 0);
AddClipboardFormatListener(winHandle);
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AddClipboardFormatListener(IntPtr hwnd);
However, when I open a window and copy stuff in it, the hookWindowMessage callback never gets triggered. Why would this be? Am I registering the window wrong, or does it need to be registered in the same process as what created the window?
AddClipboardFormatListener is returning true, which suggests it successfully registered the window.
I posted a github issue here and one of the developers suggested this might be an Electron issue, but it seemed from her response that she was just saying that Electron needed to call AddClipboardFormatListener, which I'm doing.
Why is this not working?

Xamarin Forms / MvvmCross architecture with ICommand.Execute(Object) pattern?

I've started learning some mobile development using Xamarin Forms and MvvmCross.
In its simplest form I have an app that contains a button. When this button is tapped it calls an api via a rest service and returns data via the messaging service.
I'm having issues showing dialogs (ACR) when using the ICommand.Execute(object) pattern. I'm assuming this is because the execute in this pattern is not async and therefore blocks the main ui thread? This is a sample of what I'm doing:
var cmd = Command.Create(CmdType.Login);
cmd.Execute(User);
Please consider the following lines straight from the MvvmCross documentation for async operations.
MyCommand = new MvxCommand(() => MyTaskNotifier = MvxNotifyTask.Create(() => MyMethodAsync(), onException: ex => OnException(ex)));
private async Task MyMethodAsync()
{
await _someService.DoSomethingAsync();
// ...
}
In this case my DoSomething() isn't aysnc - if anything its an async void, or a no no. To get around this I've updated from MvxCommand to MvxAsyncCommand and then have to use a bandaid to get the dialogs to show.
await Task.Delay(300);
or
await Task.Run(() => { command.Execute(User); }).ConfigureAwait(false);
At this point, I'm obviously questioning the use of the command pattern. Am I missing an easy fix or have I chosen an architecture that's not a good fit here? Any guidance is appreciated.
You can use it like this
public ICommand YourCommand => new Command(async () => await YourActionAsync());

Xamarin TouchRunner Hangs calling IOS 3rd Party Binding

I have a 3rd party API IOS Binding which I am trying to test (more like an integration test) using TouchRunner.
An example API Method is this -
_client.AuthenticateWithUsername(username, token,
() => { // Success Callback },
() => { // NoConnection Callback },
(obj) => { // Other Error Callback });
The API when called goes off and does some work in the background then eventually makes one of the callbacks above, I would like to control the flow of the unit test using something like -
How can I unit test async methods on the UI Thread with Xamarin iOS TouchRunner
Unfortunately, when I insert the AutoResetEvent code, TouchRunner just hangs and never returns to the GUI.
I have also tried to use a TaskCompletionSource as follows -
public async Task<AuthResponse> AuthenticateUserAsync(string username, string password)
{
TaskCompletionSource<AuthResponse> tcs = new TaskCompletionSource<AuthResponse>();
AuthResponse response = new AuthResponse { Success = false };
LoginResponse loginResponse = await LoginUser(username, password);
_client.AuthenticateWithUsername(username, loginResponse.token,
() =>
{
response.Success = true;
Console.WriteLine("Auth");
tcs.SetResult(response);
},
() => { tcs.SetResult(response); },
obj => { tcs.SetResult(response); },
obj => { tcs.SetResult(response); });
return await tcs.Task;
}
[Test]
public async void AuthenticateUserAsyncTest()
{
var auth = await AuthenticateUserAsync(_username, _password);
Assert.IsTrue(auth.Success);
}
The debugger stepped through fine until the return await tcs.Task, but then results in a similar HUNG runner.
How can I work out why the hang is happening?
As this was not working, I then resorted to code like this -
_client.AuthenticateWithUsername(_username, loginResponse.token,
() =>
{
Assert.Pass("This crashes the runner");
Assert.True(true); // This DOES NOT!
},
() =>
{
// This will crash runner also
Assert.Fail("NoConnection");
},
(InvalidTokenError obj) =>
{
Assert.Fail("InvalidToken" + obj.Description);
},
(ClientError obj) =>
{
Assert.Fail("ClientError" + obj.Description);
});
As you can see, the flow ends up (understandably), run test, runs client call, end of test method completes which shows test as success, then the callback returns and the asserts get called, which crash the app, which we assume is because the runner has already completed the test, why one assert works and other crashes I do not know.
So,
Am I approaching this the right way?
Could something be happening in the 3rd Party API that will cause these approaches to hang? and how would I debug that?
Thanks #Nkosi, that is a good suggestion, I forgot to add that during my original testing that when I ran the code with async Task rather than void I got an immediate block from TouchRunner without even adding any other code other than the API call! This was a red flag I suppose, but using async void "seemed" to allow "standard" async testing, so I progressed and then ended up in the loop above.
As TouchRunner has not been updated in a very long time I have just spent time re-creating the test project using XUnit after various suggestions to try it in the forums and on stack.
https://github.com/xunit/devices.xunit - runners for Xamarin IOS + Android
https://xunit.github.io/docs/comparisons - to port NUnit syntax
Some other useful links are -
https://xunit.github.io/docs/getting-started-devices.html
https://gregshackles.com/testing-xamarin-apps-getting-started-with-xunit/
https://oren.codes/2014/07/10/getting-started-with-xunit-for-xamarin/
RESULT: I am very pleased to say all the above code now works for both the TaskCompletionSource and the AutoResetTask scenarios
I can now safely test my event based API :)
I just wanted to ensure other users are aware of this.
Thanks for your help.
One observation is that the test should be async Task and not async void ie
public async Task AuthenticateUserAsyncTest() {
//...code removed for brevity.
}
async void is a fire and forget so any exceptions thrown wont happen in the current context so they wont be caught.
Reference Async/Await - Best Practices in Asynchronous Programming

Call to swift method from JavaScript hangs xcode and application

I am writing an iOS App (using xcode 7.3 and swift 2.2) using JavascriptCode framework. Calling javascript methods from swift works perfect, but when I call the swift method from javascript, xcode simply shows a "loading" type of symbol and nothing happens. I need to "force quit" xcode to get out of this state.
I have followed https://www.raywenderlich.com/124075/javascriptcore-tutorial and http://nshipster.com/javascriptcore/ and I am trying pretty simple calls.
Has anyone faced this kind of issue?
My swift code is as follows:
#objc protocol WindowJSExports : JSExport {
var name: String { get set }
func getName() -> String
static func createWindowWithName(name: String) -> WindowJS
}
#objc class WindowJS : NSObject, WindowJSExports {
dynamic var name: String
init(name: String) {
self.name = name
}
class func createWindowWithName(name: String) -> WindowJS {
return WindowJS(name: name)
}
func getName() -> String {
NSLog("getName called from JS context")
return "\(name)"
}
}
I am initializing the context as follows:
runContext = JSContext()
runContext.name = "test_Context"
windowToJs = WindowJS(name: "test")
runContext.setObject(windowToJs.self, forKeyedSubscript: "WindowJS")
If I replace the last two lines in above code with below code without instantiating it, the code simply fails to load.
runContext.setObject(WindowJS.self, forKeyedSubscript: "WindowJS")
And the javascript code is as simple as
function check() {
return WindowJS.getName()
}
I do see the breakpoint being hit in the JS function check and when the WindowJS.getName gets called, xcode simply becomes unresponsive.
The setTimeout could be solved by adding following piece of code to my swift function.
let setTimeout: #convention(block) (JSValue, Int) -> () =
{ callback, timeout in
let timeVal = Int64(timeout)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeVal), dispatch_get_main_queue(), { callback.callWithArguments(nil)})
}
To expose this native code to the JS context, I also added following.
runContext.setObject(unsafeBitCast(setTimeout, AnyObject.self), forKeyedSubscript: "setTimeout")
Things then worked fine.
You're creating a deadlock since you are calling from Swift to JavaScript back to Swift. I'm not sure exactly why it is a deadlock but I had a similar issue with WKWebView on Mac recently.
You need to decouple this and make the communication asynchronous. This obviously means you cannot simply return a value from your JS function in this case.
To decouple, you can break the deadlock by deferring the work the JavaScript function needs to do out of the current runloop iteration using setTimeout:
function myFunction() {
setTimeout(function() {
// The actual work is done here.
// Call the Swift part here.
}, 0);
}
The whole native ↔︎ JavaScript communication is very, very tricky. Avoid it if you can. There's a project called XWebView that may be able to help you as it tries to ease bridging between the two worlds.

Resources