Testing Dark Mode using snapshot testing - xcuitest

Any leads how can we use Snapshot testing to test darkmode implementation? Or any other testing strategy for dark mode on iOS.
When XCUITest is one of the options along with XCTest (unit-test). The problem with UITest in dark mode means for every app flow it needs to be run twice.
Will Unittest cases be enough to suffice testing needs for dark mode? Can you think of any use case which won't be covered using only unit testing for dark mode?

I use FBSnapshotTestCase to get light & dark snapshots in a unit test target:
final class ViewControllerSnapshotTests: FBSnapshotTestCase {
private var sut: ViewController!
override func setUp() {
super.setUp()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
sut = storyboard.instantiateViewController(
identifier: String(describing: ViewController.self)
)
usesDrawViewHierarchyInRect = true
recordMode = false // set to true to record snapshots
}
override func tearDown() {
sut = nil
super.tearDown()
}
func test_light() {
sut.overrideUserInterfaceStyle = .light
FBSnapshotVerifyViewController(sut)
}
func test_dark() {
sut.overrideUserInterfaceStyle = .dark
FBSnapshotVerifyViewController(sut)
}
}
This produced the following snapshots:
The tricks were:
Set usesDrawViewHierarchyInRect = true on the snapshot test case
Set overrideUserInterfaceStyle on the view controller
The usual advantages of snapshot tests over UITests apply. Snapshots are slower than normal unit tests, but much faster than UITests because we don't have to navigate through the app to reach each view controller.
How fast? Here's my timing, running on a 2015 MacBook Pro.
Test Suite 'ViewControllerSnapshotTests' started at 2021-04-20 21:35:26.856
Test Case '-[SOTests.ViewControllerSnapshotTests test_dark]' started.
Test Case '-[SOTests.ViewControllerSnapshotTests test_dark]' passed (0.101 seconds).
Test Case '-[SOTests.ViewControllerSnapshotTests test_light]' started.
Test Case '-[SOTests.ViewControllerSnapshotTests test_light]' passed (0.029 seconds).
Test Suite 'ViewControllerSnapshotTests' passed at 2021-04-20 21:35:26.987.
Executed 2 tests, with 0 failures (0 unexpected) in 0.130 (0.131) seconds

Related

Audio distortion occurs when using AudioWorkletProcessor with a MediaStream source and connecting a bluetooth device while it is already running

In our project, we use AudioContext to wire up input from a microphone to an AudioWorkletProcessor and out to a MediaStream. Ultimately, this is sent to other peers in a WebRTC call.
If someone loads the page, the audio always sounds fine. But if they connect with a hard-wired microphone like a laptop mic or webcam, then connect a bluetooth device (such as airpods or headphones), then the audio becomes distorted & robotic sounding.
If we tear out all the other code and simplify it, we still have the issue.
bypassProcessor.js
// Basic processor that wires input to output without transforming the data
// https://github.com/GoogleChromeLabs/web-audio-samples/blob/main/audio-worklet/basic/hello-audio-worklet/bypass-processor.js
class BypassProcessor extends AudioWorkletProcessor {
process(inputs, outputs) {
const input = inputs[0];
const output = outputs[0];
for (let channel = 0; channel < output.length; ++channel) {
output[channel].set(input[channel]);
}
return true;
}
}
registerProcessor('bypass-processor', BypassProcessor);
main.js
const microphoneStream = await navigator.mediaDevices.getUserMedia({
audio: true, // have also tried { channelCount: 1 } and { channelCount: { exact: 1 } }
video: false
})
const audioCtx = new AudioContext()
const inputNode = audioCtx.createMediaStreamSource(microphoneStream)
await audioCtx.audioWorklet.addModule('worklet/bypassProcessor.js')
const processorNode = new AudioWorkletNode(audioCtx, 'bypass-processor')
inputNode.connect(processorNode).connect(audioCtx.destination)
Interestingly, I have found if you comment out the 2 audio worklet lines and instead create a simple gain node, then it works fine.
// await audioCtx.audioWorklet.addModule('worklet/bypassProcessor.js')
// const processorNode = new AudioWorkletNode(audioCtx, 'bypass-processor')
const gainNode = audioCtx.createGain()
Also if you simply create the AudioWorkletNode, but don't even connect it to the others, this also reproduces the issue.
I've created a small React app here that reproduces the problem: https://github.com/JacobMuchow/audio_distortion_repro/tree/master
I've tried some options such as detecting when this happens using 'ondevicechange' event, closing the old AudioContext & nodes and recreating everything, but this only works some of the time. If I wait for some time and then recreate it again, it works so I'm worried about some type of garbage collection issue with the processor when attempting this, but that might be beside the point.
I suspect this has something to do with sample rates... when the AudioContext is correctly recreated it switches from 48 kHz to 16 kHz and then it sounds find. But sometimes it is recreated with 48 kHz still and it continues to sound robotic.
Threads on the internet concerning this are incredibly sparse and I'm hoping someone has specific experience with this issue or this API and can point out what I need to do differently.
For Chrome, the problem is very likely https://crbug.com/1090441 that was recently fixed. I think Firefox doesn't have this problem but I didn't check.

web audio API crashing chrome

I'm trying to build something using the processor node here. Almost anything I do in terms of debugging it crashes chrome. Specifically the tab. Whenever I bring up dev tools, and 100% of the time i put a breakpoint in the onaudioprocess node, the tab dies and I have to either find the chrome helper process for that tab or force quit chrome altogether to get started agin. Its basically crippled my development for the time being. Is this a known issue? Do I need to take certain precautions to prevent chrome from crashing? Are the real time aspects are the web audio api simply not debuggable?
Without seeing your code, it's a bit hard to diagnose the problem.
Does running this code snippet crash your browser tab?
let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
function onPlay() {
let scriptProcessor = audioCtx.createScriptProcessor(4096, 2, 2);
scriptProcessor.onaudioprocess = onAudioProcess;
scriptProcessor.connect(audioCtx.destination);
let oscillator = audioCtx.createOscillator();
oscillator.type = "sawtooth";
oscillator.frequency.value = 220;
oscillator.connect(scriptProcessor);
oscillator.start();
}
function onAudioProcess(event) {
let { inputBuffer, outputBuffer } = event;
for (let channel = 0; channel < outputBuffer.numberOfChannels; channel++) {
let inputData = inputBuffer.getChannelData(channel);
let outputData = outputBuffer.getChannelData(channel);
for (let sample = 0; sample < inputBuffer.length; sample++) {
outputData[sample] = inputData[sample];
// Add white noise to oscillator.
outputData[sample] += ((Math.random() * 2) - 1) * 0.2;
// Un-comment the following line to crash the browser tab.
// console.log(sample);
}
}
}
<button type="button" onclick="onPlay()">Play</button>
If it crashes, there's something else in your local dev environment causing you problems, because it runs perfectly for me.
If not, then maybe you are doing a console.log() (or some other heavy operation) in your onaudioprocess event handler? Remember, this event handler processes thousands of audio samples every time it is called, so you need to be careful what you do with it. For example, try un-commenting the console.log() line in the code snippet above – your browser tab will crash.

How to automate the maximize the window of an Electron application using Selenium

Currently I am performing End to end testing of an application developed using electron framework. I am able to open the application using selenium and also able to interact wit h the form controls etc. When I open the application it opens in a minimized mode and I want to maximize it by performing the keystrokes ALT + Space + X The following is my code,it executes with out any errors but does not maximize the window.
[TestMethod]
public void TestDispneseLogin()
{
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.BinaryLocation = #"C:\CorumDispense-win32-x64\CorumDispense.exe";
chromeOptions.AddArgument("start-maximized");
DesiredCapabilities capability = new DesiredCapabilities();
capability.SetCapability(CapabilityType.BrowserName, "Chrome");
capability.SetCapability("chromeOptions", chromeOptions);
IWebDriver driver = new ChromeDriver(chromeOptions);
Thread.Sleep(2000);
//maximize the window
Actions keyAction = new Actions(driver);
keyAction.SendKeys(Keys.Alt);
keyAction.SendKeys(Keys.Space);
keyAction.SendKeys(Convert.ToString('\u0078'));
keyAction.Build().Perform();
//input the text into the patient text box
driver.FindElement(By.Id("patient")).SendKeys("bharat");
}
I have also tried the option
keyAction.KeyDown(Keys.Alt).KeyDown(Keys.Space).SendKeys(Convert.ToString('\u0078')).Perform();
But it fails and gives me the following error
Test Name: TestDispneseLogin
Test FullName: LightHouseTestScenarios.TestScenarios.ElectronTest.TestDispneseLogin
Test Source: C:\Automation\SeleniumProjects\Lighthouse\LightHouseTestScenarios\TestScenarios\ElectronTest.cs : line 83
Test Outcome: Failed
Test Duration: 0:00:05.1098462
Result StackTrace:
at OpenQA.Selenium.Interactions.Internal.SingleKeyAction..ctor(IKeyboard keyboard, IMouse mouse, ILocatable actionTarget, String key)
at OpenQA.Selenium.Interactions.Actions.KeyDown(IWebElement element, String theKey)
at OpenQA.Selenium.Interactions.Actions.KeyDown(String theKey)
at LightHouseTestScenarios.TestScenarios.ElectronTest.TestDispneseLogin() in C:\Automation\SeleniumProjects\Lighthouse\LightHouseTestScenarios\TestScenarios\ElectronTest.cs:line 99
Result Message:
Test method LightHouseTestScenarios.TestScenarios.ElectronTest.TestDispneseLogin threw exception:
System.ArgumentException: key must be a modifier key (Keys.Shift, Keys.Control, or Keys.Alt)
Parameter name: key
I have also tried the below but with no success
keyAction.SendKeys(Keys.Alt + Keys.Space + Convert.ToString('\u0078')).Perform();
and also this option
driver.Manage().Window.Maximize();
Can some one help me solving this issue,thanks in advance.
cheers,
bharadwaj.
When you need using electron features, the easy way to do it is working with the executeScript method.
instead:
driver.Manage().Window.Maximize();
replace with:
driver.executeScript("require('electron').remote.BrowserWindow.getFocusedWindow().maximize();");
For python-selenium implementation, following worked for Electron:
self.session.execute_script('window.moveTo(0, 0);window.resizeTo(screen.width, screen.height);')
It does not exactly maximize the window but sets the window size equal to screen size. Might not work for all types of applications.
try this code.. Its working for me...
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("require('electron').remote.BrowserWindow.getFocusedWindow().maximize();");

NSJSONSerialization.JSONObjectWithData leaks memory

I have a function which uses NSJSONSerialization.JSONObjectWithData, but some memory was not released.
So I tracked down where the leak occurred and tested it with the following function:
private func test() {
for var i = 0; i < 100000; i++ {
let toParse = NSString(string: "{ \"Test\" : [ \"Super mega long JSON-string which is super long because it should be super long because it is easier to see the differences in memory usage with a super long JSON-string than with a short one.\" ] }").dataUsingEncoding(NSUTF8StringEncoding)!
let json = try! NSJSONSerialization.JSONObjectWithData(toParse, options: NSJSONReadingOptions(rawValue: 0))
}
}
The memory-usage of my app before I called test() was 11 MB, the memory-usage after was 74.4 MB (even if I did some other things in my app to give the system some time to release the memory)...
Why is json not released?
Mundi pointed me to autoreleasepool which I hadn't tried yet (insert facepalm here)... so I changed the code to:
autoreleasepool {
self.test()
}
This didn't make any difference, and because Xcode suggested it, I also tried:
autoreleasepool({ () -> () in
self.test()
})
But this also didn't work...
P.S.: Maybe I should add that I'm using Swift 2.0 in Xcode 7 GM.
P.P.S: The test()-function is called from within a
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), {
//... my code ...
self.test()
})
but this shouldn't make any difference...
You are misunderstanding how an autorelease pool works. An autorelease pools keeps hold of allocated memory until the pool is released. Calling a loop 100,000 times inside an autorelease pool means the pool has no chance to release anything, so the memory builds up. Eventually it goes away, when the code has finished running and the autorelease pool is released, but meanwhile your memory usage goes up.
Correct way:
private func test() {
for var i = 0; i < 100000; i++ {
autoreleasepool {
stuff
}
}
}
As you point out in your question, the app arbitrarily releases the memory, so the fact that it is still not release does not mean it would cause a tight memory condition.
You can try enclosing your test routine in an autoreleasepool, similar to Objective-C.
func test() {
autoreleasepool {
// do the test
}
}

Appium - running browser tests without clearing browser data

I'm testing a web application on Chrome, Android (real device, not emulator) using Appium. Whenever I launch a test, all browser data (bookmarks, history etc.) is deleted. Is there any way to stop this from happening?
I tried setting the noReset capability to true, but that didn't help.
Thank you in advance for any help
public static Uri testServerAddress = new Uri("http://127.0.01:4723/wd/hub"); // Appium is running locally
public static TimeSpan INIT_TIMEOUT_SEC = TimeSpan.FromSeconds(180);
public void SetUpTest()
{
if (driver == null)
{
DesiredCapabilities testCapabilities = new DesiredCapabilities();
testCapabilities.SetCapability("browserName", "Chrome");
testCapabilities.SetCapability("platformName", "Android");
testCapabilities.SetCapability("deviceName", "S(Galaxy S5)");
testCapabilities.SetCapability("noReset", true);
AppUrl = "http://www.google.com/"; //for example
driver = new RemoteWebDriver(testServerAddress, testCapabilities, INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, globalTimeoutInSec));
driver.Navigate().GoToUrl(AppUrl);
}
}
Chromedriver always starts totally fresh, nothing is keeping.
There is option to re-use the existent one (using desired capability androidUseRunningApp) but unfortunately Appium any way will kill it.
Please see more details in this post