New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple pool cypress test migration #7616
Multiple pool cypress test migration #7616
Conversation
Hi @GowthamShanmugam. Thanks for your PR. I'm waiting for a openshift member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
86e59ce
to
b70c2dc
Compare
|
||
// Check ceph cluster status | ||
cy.exec(`oc get cephCluster -n ${NS} -o json`).then((res) => { | ||
const cephValue = JSON.parse(res.stdout.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const cephValue = JSON.parse(res.stdout.toString()); | |
const cephValue = JSON.parse(res.stdout); |
I don't think if toSting()
is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
|
||
// View create storage class form |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// View create storage class form |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
}); | ||
|
||
afterEach(() => { | ||
checkErrors(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkErrors(); | |
cy.checkErrors(); |
If you do it this way you won't have to add an import statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But cy dont have any function with name checkErrors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my bad it was previously a cypress command looks like it's no more.
}); | ||
|
||
it('Check for a new pool creation', () => { | ||
if (cephStatus?.status?.phase === CLUSTER_STATUS.READY) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are we ending up with a state when Ceph Cluster isn't in Ready
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this check is to handle to corner case, as storage cluster creation takes a longer time than usual and parallelly if someone tries to create the pool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, cy.install() always make sure the cluster is in a ready state. So I remove this block.
} else if (cephStatus?.status?.phase !== CLUSTER_STATUS.READY) { | ||
// This block will execute only if cluster is not in ready state | ||
cy.log('Trying to create a new pool when cluster is not in ready state'); | ||
storagePool.prepareStorageClassForm(provisioner); | ||
|
||
// Validations | ||
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, "PROGRESS")); | ||
// Close pool creation form | ||
cy.byTestID(cancelAction).click(); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} else if (cephStatus?.status?.phase !== CLUSTER_STATUS.READY) { | |
// This block will execute only if cluster is not in ready state | |
cy.log('Trying to create a new pool when cluster is not in ready state'); | |
storagePool.prepareStorageClassForm(provisioner); | |
// Validations | |
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, "PROGRESS")); | |
// Close pool creation form | |
cy.byTestID(cancelAction).click(); | |
}; | |
} else if (cephStatus?.status?.phase !== CLUSTER_STATUS.READY) { | |
// This block will execute only if cluster is not in ready state | |
cy.log('Trying to create a new pool when cluster is not in ready state'); | |
storagePool.prepareStorageClassForm(provisioner); | |
// Validations | |
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, "PROGRESS")); | |
// Close pool creation form | |
cy.byTestID(cancelAction).click(); | |
}; |
I realize this is a part of the original tests. But I am not sure why we are doing this check? How can we enforce that Ceph CLuster is indeed in a Progressing
state. Not knowing the exact state of the application can be a source of flakes. Tagging @a2batic to understand more about this test.
it('Should throw an error if duplicate pool is created', () => { | ||
cy.log('Try to creating a new pool with already existing name'); | ||
storagePool.prepareStorageClassForm(provisioner); | ||
storagePool.create(poolName, replicaCount); | ||
|
||
// Validations | ||
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, "POOL_DUPLICATED")); | ||
// Close pool creation form | ||
cy.byTestID(confirmAction).click(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it('Should throw an error if duplicate pool is created', () => { | |
cy.log('Try to creating a new pool with already existing name'); | |
storagePool.prepareStorageClassForm(provisioner); | |
storagePool.create(poolName, replicaCount); | |
// Validations | |
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, "POOL_DUPLICATED")); | |
// Close pool creation form | |
cy.byTestID(confirmAction).click(); | |
}); | |
it('Should throw an error if duplicate pool is created', () => { | |
cy.log('Try to creating a new pool with already existing name'); | |
storagePool.prepareStorageClassForm(provisioner); | |
storagePool.create(poolName, replicaCount); | |
// Validations | |
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, "POOL_DUPLICATED")); | |
// Close pool creation form | |
cy.byTestID(confirmAction).click(); | |
}); |
This too should be part of the same it
block. As for this to work we need the previous it
block to run.
getPoolMessage: (poolName: string, status: string) => { | ||
return poolMessage.get(status).replace('$PoolName', poolName); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getPoolMessage: (poolName: string, status: string) => { | |
return poolMessage.get(status).replace('$PoolName', poolName); | |
}, | |
getPoolMessage: (poolName: string, status: string) => poolMessage.get(status).replace('$PoolName', poolName); | |
export const createStorageClassButton: string = 'item-create'; | ||
|
||
// Create storage class from | ||
export const provisionerDropdown: string = 'storage-class-provisioner-dropdown'; | ||
export const expandPvcCheckbox: string = 'expand-pvc-checkbox'; | ||
export const storagePoolDropdown: string = 'pool-dropdown-toggle'; | ||
export const createNewPoolButton: string = 'create-new-pool-button'; | ||
|
||
// Create new pool form | ||
export const titleLabel: string = 'modal-title'; | ||
export const poolNameTextBox: string = 'new-pool-name-textbox'; | ||
export const replicaDropdown: string = 'replica-dropdown'; | ||
export const confirmAction: string = 'confirm-action'; | ||
export const cancelAction: string = 'modal-cancel-action'; | ||
|
||
// Pool status | ||
export const emptyStateBody: string = 'empty-state-body'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Constants that are only referenced in one place, don't deserve to be declared as constants. We can directly use them inside the getters. The abstraction provided by storagePool
object is good enough. We don't need to reference selectors from here if it's only done once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed but what if new test cases are written in the future for the same feature. It always better to write a loosely coupled code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you, but I am not sure if we will have a use case for pool flows again. If it's required in other tests most of the time we will be creating pools via CLI as it's much easier( and less error-prone). Our constants folder is full of constants that are used in only one place. It'd be better if we refactor it into a constant later when we require it, as the chances of these being used again are quite slim.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed constants which are all used in one place, please review
export const poolMessage: Map<string, string> = new Map([ | ||
[ | ||
'PROGRESS', | ||
'The creation of an OCS storage cluster is still in progress or have failed please try again after the storage cluster is ready to use.', | ||
], | ||
['POOL_START', 'Pool $PoolName creation in progress'], | ||
['POOL_DUPLICATED', 'Pool "$PoolName" already exists'], | ||
['POOL_CREATED', 'Pool $PoolName was successfully created'], | ||
]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the usage of Map a bit of an overkill here. We could just use the plain JavaScript object. As I can see the keys are plain strings. I also don't see the need of maintaining insertion order.
storagePool, | ||
} from '../views/multiple-pool'; | ||
|
||
describe('Test ceph pool creation', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
describe('Test ceph pool creation', () => { | |
describe('Test Ceph pool creation', () => { |
f3a79a1
to
ee58877
Compare
/retest |
@GowthamShanmugam: Cannot trigger testing until a trusted user reviews the PR and leaves an In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
|
||
describe('Test ceph pool creation', () => { | ||
|
||
// Ceph status var |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Ceph status var | |
// Ceph status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
describe('Test ceph pool creation', () => { | ||
|
||
// Ceph status var | ||
let cephStatus:any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let cephStatus:any; | |
let cephStatus:string; |
// Check ceph cluster status | ||
cy.exec(`oc get cephCluster -n ${NS} -o json`).then((res) => { | ||
const cephValue = JSON.parse(res.stdout.toString()); | ||
cephStatus = cephValue.items[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cephStatus = cephValue.items[0]; | |
cephStatus = cephValue?.items?.[0]; |
// Pool var | ||
const poolName: string = 'example.com'; | ||
const replicaCount: string = '2'; | ||
const provisioner: string = 'openshift-storage.rbd.csi.ceph.com'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const provisioner: string = 'openshift-storage.rbd.csi.ceph.com'; | |
const RBDProvisioner: string = 'openshift-storage.rbd.csi.ceph.com'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
let cephStatus:any; | ||
|
||
// Pool var | ||
const poolName: string = 'example.com'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const poolName: string = 'example.com'; | |
const poolName: string = 'example-pool'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
6862fd2
to
267adbb
Compare
before(() => { | ||
cy.login(); | ||
cy.visit('/'); | ||
// cy.install(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// cy.install(); | |
cy.install(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! ack
cy.byTestID(createNewPoolButton).click(); | ||
}, | ||
create: (poolName: string, replicaCount: string) => { | ||
// Makesure the storage pool creation form is open |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Makesure the storage pool creation form is open | |
// Make sure the storage pool creation form is open |
// Select provisioner | ||
cy.byTestID(provisionerDropdown) | ||
.click() | ||
.get(`a[id="${RBDProvisioner}-link"]`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.get(`a[id="${RBDProvisioner}-link"]`) | |
.get(`a#${RBDProvisioner}-link`) |
You don't really need the a
though.
const regex = new RegExp(`${poolDropdownItem.join('|')}`, 'g'); | ||
storagePool.validate(poolName, regex); | ||
|
||
console.log('Verifying a pool created in backend or not'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
console.log('Verifying a pool created in backend or not'); | |
cy.log('Verifying a pool created in backend or not'); |
|
||
// Pool status | ||
export const emptyStateBody: string = 'empty-state-body'; | ||
export const poolStatus: string = 'Ready'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of the variable doesn't truly reflect what it does. It would be better if it were an enum
.
export const poolStatus: string = 'Ready'; | |
export enum PoolStatus { | |
READY = 'Ready' | |
} |
delete: (poolName: string) => { | ||
cy.exec(`oc delete CephBlockPool ${poolName} -n ${NS}`); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete: (poolName: string) => { | |
cy.exec(`oc delete CephBlockPool ${poolName} -n ${NS}`); | |
}, | |
delete: (poolName: string) => cy.exec(`oc delete CephBlockPool ${poolName} -n ${NS}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
cy.byTestID('create-new-pool-button').click(); | ||
}, | ||
create: (poolName: string, replicaCount: string) => { | ||
// Makesure the storage pool creation form is open |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this shows up as a modal. Perhaps you can reuse methods form https://github.com/openshift/console/blob/master/frontend/packages/integration-tests-cypress/views/modal.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
PROGRESS: | ||
'The creation of an OCS storage cluster is still in progress or have failed please try again after the storage cluster is ready to use.', | ||
POOL_START: 'Pool $PoolName creation in progress', | ||
POOL_DUPLICATED: 'Pool "$PoolName" already exists', | ||
POOL_CREATED: 'Pool $PoolName was successfully created', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PROGRESS: | |
'The creation of an OCS storage cluster is still in progress or have failed please try again after the storage cluster is ready to use.', | |
POOL_START: 'Pool $PoolName creation in progress', | |
POOL_DUPLICATED: 'Pool "$PoolName" already exists', | |
POOL_CREATED: 'Pool $PoolName was successfully created', | |
PROGRESS: () => | |
'The creation of an OCS storage cluster is still in progress or have failed please try again after the storage cluster is ready to use.', | |
POOL_START: (poolname) => `Pool ${poolName} creation in progress`, | |
POOL_DUPLICATED: (poolName) => `Pool "${poolName}" already exists', | |
POOL_CREATED: (poolName) => `Pool ${poolName} was successfully created`, |
Doing this should allow us to remove getPoolMessage
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
}, | ||
create: (poolName: string, replicaCount: string) => { | ||
// Makesure the storage pool creation form is open | ||
cy.byLegacyTestID('modal-title').contains('Create New Storage Pool'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cy.byLegacyTestID('modal-title').contains('Create New Storage Pool'); | |
modalTitleShouldContain('Create New Storage Pool'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, 'POOL_START')); | ||
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, 'POOL_CREATED')); | ||
// Close pool creation form | ||
cy.get(`#${confirmAction}`).click(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, 'POOL_START')); | |
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, 'POOL_CREATED')); | |
// Close pool creation form | |
cy.get(`#${confirmAction}`).click(); | |
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, 'POOL_START')); | |
storagePool.validate(emptyStateBody, storagePool.getPoolMessage(poolName, 'POOL_CREATED')); | |
// Close pool creation form | |
cy.get(`#${confirmAction}`).click(); |
storagePool.create
should have this block as it's part of creating a pool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
9a5abf1
to
2d05eb2
Compare
modal.submitShouldBeEnabled(); | ||
|
||
// Create new pool | ||
cy.get('#confirm-action').click(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unable to use the submit method from modals. The storage class creation page and the pool creation model both have submit button.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If two elements are returned we could chain first
or last
and then get the required element.
2d05eb2
to
5b1dfb9
Compare
/retest |
@GowthamShanmugam: Cannot trigger testing until a trusted user reviews the PR and leaves an In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
5b1dfb9
to
be3df0b
Compare
@cloudbehl @afreen23 @bipuladh all changes are done please review |
/ok-to-test |
cy.byTestID(storagePoolDropdown).click(); | ||
const poolDropdownItem = [poolName, `Replica ${replicaCount} no compression`]; | ||
const regex = new RegExp(`${poolDropdownItem.join('|')}`, 'g'); | ||
storagePool.validate(poolName, regex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dint know contains supported regex. nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recently introduced change in console.
|
||
beforeEach(() => { | ||
cy.visit('/'); | ||
cy.clickNavLink(['Storage', 'Storage Classes']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cy.clickNavLink(['Storage', 'Storage Classes']); | |
cy.clickNavLink(['Storage', 'StorageClasses']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
Please run prettier on the changed files as well. (frontend test is failing as well) |
be3df0b
to
e474a72
Compare
@bipuladh solved frontend test is failiers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm cancel
}); | ||
|
||
after(() => { | ||
storagePool.delete(poolName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should make this part of the it block as creation is part of it already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
// Select provisioner | ||
cy.byTestID('storage-class-provisioner-dropdown') | ||
.click() | ||
.get('#openshift-storage\\.rbd\\.csi\\.ceph\\.com-link') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<a href="#" id="openshift-storage.rbd.csi.ceph.com-link" data-test="dropdown-menu-item-link" class="pf-c-dropdown__menu-item">openshift-storage.rbd.csi.ceph.com</a>
The element as rendered in the DOM
Perhaps something like
cy.byTestID('dropdown-menu-item-link').contains('openshift-storage.rbd.csi.ceph.com')
modal.submitShouldBeEnabled(); | ||
|
||
// Create new pool | ||
cy.get('#confirm-action').click(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If two elements are returned we could chain first
or last
and then get the required element.
e474a72
to
e869e42
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm, just some small suggestions
cy.visit('/'); | ||
cy.clickNavLink(['Storage', 'StorageClasses']); | ||
cy.byTestID('item-create').click(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Little confused why beforeEach
is needed since there is only one test? Suggest moving this to top of single it
test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
.type(poolName); | ||
cy.byTestID(replicaDropdown) | ||
.click() | ||
.byLegacyTestID(replicaCount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please note that byLegacyTestID
and byTestID
are Parent Commands and not Child or Dual Commands, as such the chaining here (the cy.get in byLegacyTestID) will start from the root of the DOM, not from replicaDropdown
.
So if there was another 'count' link outside of the replicaDropdown
which could have, for example < ... data-test-id="3" />
it may be selected.
I've been meaning to convert these to Dual Commands, if your up for it, now would be a good time! :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack
Soigned-off-by: gshanmug <gshanmug@redhat.com>
e869e42
to
bedae20
Compare
@dtaylor113 Changes done, please review |
@spadgett Please review |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: dtaylor113, GowthamShanmugam, rawagner The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Signed-off-by: Gowtham Shanmugasundaram gshanmug@localhost.localdomain