How to get extensionContext in vscode extension unit test? - unit-testing

Currently I am writing unit tests about vscode extension. But some functions are using extensionContext and I can't get extensionContext in unit tests. Any way to get it?

Just came across this question, because I had exactly the same problem.
It looks like you can do the following in a test:
const ext = vscode.extensions.getExtension("publisher.extensionName");
And you can return anything from your activate function, so you could decide to return the extension context (or anything else that you need) there:
export async function activate(
context: vscode.ExtensionContext
): Promise<vscode.ExtensionContext> {
// Your activation code...
return context;
}
And then you can access the context in the test:
const ext = vscode.extensions.getExtension("publisher.extensionName");
const myExtensionContext = await ext.activate();
You find publisher.extensionName information in package.json of the extension:
{
"publisher": "myself",
"name": "myextension",
"displayName": "My Extension",
"description": "",
"version": "1.0.0",
...

Another idea is to add a command that returns the context:
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('getContext', () => context));
}
and then get the context by:
const context = await vscode.commands.executeCommand("getContext") as vscode.ExtensionContext;
But be careful not to expose the context in production

This is an answer to an old question, but I've had the same problem and I'm answering it now, so others can see it.
I found this solution in https://github.dev/microsoft/vscode extensions/vscode-api-test/src/extension.ts and extensions/vscode-api-test/src/singlefolder-test/state.test.ts files.
First, the context entered in the activate function must be registered in the global as shown below.
import * as vscode from 'vscode';
export function activate(_context: vscode.ExtensionContext) {
// Set context as a global as some tests depend on it
(global as any).testExtensionContext = _context;
}
After that, you can use it as follows in the file to be tested.
import * as assert from 'assert';
import 'mocha';
import { ExtensionContext, extensions } from 'vscode';
suite('vscode API - globalState / workspaceState', () => {
let extensionContext: ExtensionContext;
suiteSetup(async () => {
// Trigger extension activation and grab the context as some tests depend on it
await extensions.getExtension('vscode.vscode-api-tests')?.activate();
extensionContext = (global as any).testExtensionContext;
});
test('state', async () => {
// Do some tests here using extensionContext
}
});
});
In the signature of the activate function, context is received as a parameter, but I don't know why, but here it can be used without setting context as a parameter. It may be that the corresponding value is entered automatically.

Related

How to run a single test suite in Jest?

I have many test suites. I wanna to run a singe one and skip all the others, and I would like to do this on the code level.
I know I can do this using .only() and .skip() in a test file, but that supports only the tests / describes defined in that file.
Is there a way to do this globally? Like is there something like .only() which - when called on the top level describe - runs only that test suite and all others are skipped?
Or: when called on a single test ( it().only() ), then only that test runs and nothing else?
I see nothing like this in the API, but maybe Jest can be configured to work this way?
Is this possible with Jest or is this something I can only do via CLI?
If I understand correctly: You want to run just one test suite/file.
You can do this from the command line with jest path/to/filename.test.js.
Within a file, you can use test.only(name, fn, timeout) to only run that test. This won't stop Jest from moving on to the next testing file though.
Full Jest CLI docs
As far as I am aware, you cannot do this from within the test file itself.
The closest I can think of would be to set the `testmatch' in Jest's config to a pattern that only matches the file(s) you want run.
package.json
{
"name": "my-project",
"jest": {
"testmatch": "**/my.test.js"
}
}
I think if you adapt this answer: https://stackoverflow.com/a/59487370/14553660 it should give you what you need.
For example:
testsuite.test.js
import { signuptests } from './signup.test.js'
import { logintests } from './login.test.js'
import { logouttests } from './logout.test.js'
describe('Signup', signuptests)
describe.only('Login', logintests)
describe('Logout', logouttests)
signup.test.js
export const signuptests = () => {
it('Should have login elements', () => {});
it('Should Signup', () => {}});
}
login.test.js
export const logintests = () => {
it.only('Should Login', () => {}});
it('etc',()=>{});
}
logout.test.js
export const logouttests = () => {
it('Should Logout', () => {}});
it('etc',()=>{});
}
You will be able to use .only at the top level - in the testsuite file - to determine which test files are run, and you can also use .only within each individual test file (e.g. within login.test.js) to only run one particular test from that file.
I don't know that there is a limit to nesting describe blocks, so I imagine you could even make a 'master-testsuite' that imports different testsuites (which in turn import different test files...etc)

How can I spy on TouchableNativeFeedback onPress in React Native with #testing-library/react-native?

Help me please...
I have a React Native custom Button component that receives an onPress prop and passes that onPress to a TouchableNativeFeedback. I make a unit testing with #testing-library/react-native. I try to spy on onPress, to check if it is triggered when the TouchableNativeFeedback is touched. Somehow the spy function is not called when I simulate press event on the Button. Is there any mistake in my implementation?
FI: This behavior only occurs in TouchableNativeFeedback, but working perfectly with Button
Here is my implementation:
Button.js
const Button = ({onPress, label}) => (
<TouchableNativeFeedback onPress={onPress} testID="button">
<View style={someStyling}>
<Text>{label}</Text>
</View>
</TouchableNativeFeedback>
)
export default Button
Button.test.js
it("Fire onPress props when button pressed", () => {
const mockFn = jest.fn();
const { getByTestId } = render(<Button onPress={mockFn} />);
const button = getByTestId("button");
fireEvent.press(button);
expect(mockFn).toHaveBeenCalled();
});
I had the same issue in a component using <TouchableNativeFeedback/>. My test case failed with a message saying No instances found on the getByText call.
After few attempts, I used following snippet for debugging:
const { debug } = render(<Tag onPress={handler}>Press me</Tag>);
debug();
Along with some other details, following message popped up in console:
TouchableNativeFeedback is not supported on this platform!
At this moment, there is a related thread regarding Touchable* components on react-native-testing-library GH issues where they explain details and provide some workarounds.
I ended up using platform detection in my component, as that's what I wanted to do anyway:
const TouchComponent =
Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity;
return (
<TouchComponent onPress={onPress}>
...
</TouchComponent>
);
I had the same issue.
What solved it for me was that fact that my TouchableXXX component was imported fromreact-native-gesture-handler. This library probably was imported as dependency by one of my dependecies. I guess when I wrote TouchableXXX it was imported automatically from there by my IDE.
After changing the import statement to import from react-native - everything worked for me in tests.
So eventually my change was from this:
import { TouchableHighlight } from 'react-native-gesture-handler'
to this
import { TouchableOpacity } from 'react-native'

How to mock global Vue.js variable in JEST test

I have a global property/variable with my app urls:
Vue.prototype.$apiUrls = {
root: 'http://localhost:8080/',
api: 'api/v1/'
// etc.
}
I use it inside my components as axios request:
axios.get(`${this.$apiUrls.root}${this.$apiUrls.api}/users/`)
Now I want to test my component's code, I've mocked axios already, but still I receive an error:
TypeError: Cannot read property '$apiUrls' of undefined
I've tried to define/mock this property inside each test and/or in JEST's setup file, like e.g.
global.$apiUrls = {...}
// or
Vue.prototype.$apiUrls = {...}
// or
Object.defineProperties(Vue.prototype, {$apiUrls: {...}})
I've also tried mocking it to window or this (yeah, thats silly), but with no success - I still receive that error - please help.
There is two ways to achieve this. One is using the Config option, as mentioned by #Aldarund. You can read about it here.
If you are using Jest, I recommend doing this in the jest.init.js file:
import { config } from '#vue/test-utils'
config.mocks['$apiUrls'] = {
'some/endpoint'
}
Then add this to the jest section of your package.json:
"setupFiles": [
"<rootDir>/jest.init.js"
]
Now it is globally mocked. If you want to do this on a per test basis, you can use the mocks mounting option:
const wrapper = shallowMount(Foo, {
mocks: {
$apiUrls: 'some/endpoint'
}
})
Hopefully this helps!
If you are interested I am compiling a collection of simple guides on how to test Vue components here. It's under development, but feel free to ask make an issue if you need help with other related things to testing Vue components.
I don't think the answers above work anymore (in 2020).
Here's what worked for me:
For vue-test-utils 1.x.x (Vue 2)
Create a new file, name it eg. jest.init.js
Give it the following content:
import { config } from "#vue/test-utils";
config.mocks["yourGlobalProperty"] = label => label; //you can replace it with your own mock
Add this to your jest.config.js (actually write "rootDir", don't replace anything with a real path)
module.exports = {
setupFiles: ["<rootDir>/jest.init.js"]
}
These files will be only ran before jest runs unit tests.
Note that I'm importing {config}, not the default export. I don't know why the default didn't work for me. Even the documentation for vue test utils doesn't import the default export anymore
Also make sure you're not trying to import from the old vue-test-utils package. (The new one is #vue/test-utils)
For #vue/test-utils 2.x.x (vue-test-utils-next) (Vue 3)
Follow steps like for 1.x.x above, but in step two, do this instead:
import { config } from "#vue/test-utils"; //2.0.0-beta.5
config.global.mocks = {
yourGlobalProperty: label => label
};
You can do it with vue-test-utils beta 15 and later.
Here docs
And some example would be:
import VueTestUtils from '#vue/test-utils'
VueTestUtils.config.mocks['$apiUrls'] = {
...
}

How to access global constant in unit tests with Jest?

I want to mock a global variable and test if it has been called in my function. In my file, I define a global variable "remote" which instanciates a Subject (RxJS library). I just want to test if the next function of Subject has been called with the right parameter "name". However, I can't access the global variable remote in my test file. I tried to mock it in my setup file, but doesn't work.
How can I do that ?
const remote = new Subject();
const analytics = {
click: (name) => {
if (name) {
remote.next(name);
}
}
}
module.exports = analytics;
Thanks !
This is a good question. When you use webpack, your individual file is wrapped into different function call. Check out this doc.
The best part of this question is that it shows the necessity of IOC/DI if you want your code to be testable. Instead of defining remote in your local module, you can export an Analytics class and then inject remote to its constructor.
// Analytics.js
export default class Analytics{
constructor(remote) {
this.remote = remote
}
click(name) {
if (name) {
this.remote.next(name)
}
}
}
main.js
import Analytics from './Analytics'
const remote = new Subject()
const analytics = new Analytics(remote)
analysis.click('foo')
It would be tedious to inject dependencies to all components/services. Angular has an decent doc on why/how to simplify it. Hope this is helpful!
// Update
You can use window to define global constant and access it in tests by using window.remote
const remote = new Subject()
window.remote = remote
When you want to mock remote, remember to modify its properties rather than the reference to it.
// test.js
beforeEach(() => {
// wrong !!!
window.remote = {
next(name) { assert(name) }
}
// right
window.remote.next = name => assert(name)
})

Is it possible to mock a controller and have access to the FormTagLib in the same unit test?

As the title states, is it possible to have a unit test for a controller, and mock a tag lib?
As it stands, I have a User controller. Many of the actions use the
g.message(code: 'something.something')
call to set a message on the page.
#TestFor(UserController)
#Mock(User)
#TestMixin(GroovyPageUnitTestMixin)
class UserControllerSpec extends Specification
{
UserService userServiceMock = Mock(UserService)
def setup()
{
controller.userService = userServiceMock
}
def cleanup()
{
}
void "test manageDevice"()
{
given:
def g = mockTagLib(FormTagLib)
when:
controller.manageDevice()
then:
model.pageTitle == 'message.device'
}
With that code I'm trying to hit the controller action but because of the g.message, it's failing with an error saying that it can't set a value to null. Pretty much because it doesn't see the "g.message"
I'm a little unsure if my unit test needs written differently, or if I'm just missing something.
Any help would be great!
EDIT:
Some updates using messageSource:
void "test manageDevice"()
{
given:
messageSource.addMessage 'user.devices', request.locale, 'Manage Devices'
when:
controller.manageDevices()
then:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
}
It seems to still be complaining because it doesn't have context of the "g" namespace on the controller. I'll note as well, I don't have context of 'addMessage' from messageSource. Not sure why, it should be there.
In the same controller, and many others, the only taglib we use in the controller scope is 'g' for the 'g.message' set in each action. The only other call that's being done, is one using the 'g' for a call like 'g.fixRedisIssue'
You can make a unit test for a controller with a mocked TagLib by including the necessary TagLib classes in the value of the grails.test.mixin.Mock annotation on the Spec class. For example, if you have the following TagLib:
(based on code in Grails documentation)
package org.grails.samples
class SimpleTagLib {
static namespace = 'g'
def hello = { attrs, body ->
out << "Hello ${attrs.name ?: 'World'}"
}
}
And you have the following controller:
package org.grails.samples
class SimpleController {
def flashHello() {
flash.message = g.hello()
}
}
You could test it with the following specification:
package org.grails.samples
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SimpleController)
#Mock(SimpleTagLib)
class SimpleControllerSpec extends Specification {
void 'test flashHello'() {
when:
controller.flashHello()
then:
flash.message == 'Hello World'
}
}
If you have multiple classes to mock, you can specify them in an array argument to #Mock. So you can add any necessary tag libraries to UserControllerSpec by changing #Mock(User) to something like #Mock([User, FormTagLib, YourFixRedisIssueTagLib]). However, I'd be surprised if you actually did need to add FormTagLib because in my Grails testing it seems to be included by default.
As some further advice, some of the code you posted doesn't make any sense. Specifically:
assertEquals model.pageTitle == 'user.devices', controller.flash.message
Decide specifically what you want to test for and focus on writing the simplest code possible for that.
Also, based on your description of what's happening, I think that you're getting thrown off because your IDE is not properly integrated with Grails to see the g context.
Instead of returning the message text to the view from the controller, why not return the message code instead? For example, if you have something like this in your view:
<p>${flash.message}</p>
You can replace it with this:
<p><g:message code="${flash.code}" /></p>
And then set the code in the controller:
flash.code = "user.devices"
Then you'll be able to test the controller methods painlessly :)