@eclipse-che/che-e2e
Advanced tools
Comparing version
@@ -247,25 +247,25 @@ "use strict"; | ||
} | ||
async waitErrorInLine(lineNumber, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
async waitErrorInLine(lineNumber, fileName, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
Logger_1.Logger.debug(`Editor.waitErrorInLine line: "${lineNumber}"`); | ||
const errorInLineLocator = await this.getErrorInLineLocator(lineNumber); | ||
const errorInLineLocator = await this.getErrorInLineLocator(lineNumber, fileName); | ||
await this.driverHelper.waitVisibility(errorInLineLocator, timeout); | ||
} | ||
async waitErrorInLineDisappearance(lineNumber, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
async waitErrorInLineDisappearance(lineNumber, fileName, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
Logger_1.Logger.debug(`Editor.waitErrorInLineDisappearance line: "${lineNumber}"`); | ||
const errorInLineLocator = await this.getErrorInLineLocator(lineNumber); | ||
const errorInLineLocator = await this.getErrorInLineLocator(lineNumber, fileName); | ||
await this.driverHelper.waitDisappearanceWithTimeout(errorInLineLocator, timeout); | ||
} | ||
async waitWarningInLine(lineNumber, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
async waitWarningInLine(lineNumber, fileName, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
Logger_1.Logger.debug(`Editor.waitWarningInLine line: "${lineNumber}"`); | ||
const warningInLineLocator = await this.getWarningInLineLocator(lineNumber); | ||
const warningInLineLocator = await this.getWarningInLineLocator(lineNumber, fileName); | ||
await this.driverHelper.waitVisibility(warningInLineLocator, timeout); | ||
} | ||
async waitInfoInLine(lineNumber, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
async waitInfoInLine(lineNumber, fileName, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
Logger_1.Logger.debug(`Editor.waitInfoInLine line: "${lineNumber}"`); | ||
const infoInLineLocator = await this.getInfoInLineLocator(lineNumber); | ||
const infoInLineLocator = await this.getInfoInLineLocator(lineNumber, fileName); | ||
await this.driverHelper.waitVisibility(infoInLineLocator, timeout); | ||
} | ||
async waitWarningInLineDisappearance(lineNumber, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
async waitWarningInLineDisappearance(lineNumber, fileName, timeout = TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT) { | ||
Logger_1.Logger.debug(`Editor.waitWarningInLineDisappearance line: "${lineNumber}"`); | ||
const warningInLineLocator = await this.getWarningInLineLocator(lineNumber); | ||
const warningInLineLocator = await this.getWarningInLineLocator(lineNumber, fileName); | ||
await this.driverHelper.waitDisappearanceWithTimeout(warningInLineLocator, timeout); | ||
@@ -311,5 +311,6 @@ } | ||
} | ||
async getLineYCoordinates(lineNumber) { | ||
async getLineYCoordinates(lineNumber, fileName) { | ||
Logger_1.Logger.debug(`Editor.getLineYCoordinates line: "${lineNumber}"`); | ||
const lineNumberLocator = selenium_webdriver_1.By.xpath(`//div[contains(@class, 'line-numbers') and text()='${lineNumber}']` + | ||
const lineNumberLocator = selenium_webdriver_1.By.xpath(`//div[contains(@data-uri, '${fileName}')]` + | ||
`//div[contains(@class, 'line-numbers') and text()='${lineNumber}']` + | ||
`//parent::div[contains(@style, 'position')]`); | ||
@@ -324,5 +325,5 @@ let elementStyleValue = await this.driverHelper.waitAndGetElementAttribute(lineNumberLocator, 'style', TimeoutConstants_1.TimeoutConstants.TS_SELENIUM_CLICK_ON_VISIBLE_ITEM); | ||
} | ||
async clickOnLineAndChar(line, char) { | ||
async clickOnLineAndChar(line, char, fileName) { | ||
Logger_1.Logger.debug(`Editor.clickOnLineAndChar line: "${line}" char: "${char}"`); | ||
const yPosition = await this.getLineYCoordinates(line) + Editor_1.ADDITIONAL_SHIFTING_TO_Y; | ||
const yPosition = await this.getLineYCoordinates(line, fileName) + Editor_1.ADDITIONAL_SHIFTING_TO_Y; | ||
const xPosition = char + Editor_1.ADDITIONAL_SHIFTING_TO_X; | ||
@@ -334,5 +335,5 @@ new selenium_webdriver_1.ActionSequence(this.driverHelper.getDriver()). | ||
} | ||
async goToDefinitionWithMouseClicking(line, char) { | ||
async goToDefinitionWithMouseClicking(line, char, fileName) { | ||
Logger_1.Logger.debug(`Editor.goToDefinitionWithMouseClicking line: "${line}" char: "${char}"`); | ||
const yPosition = await this.getLineYCoordinates(line) + Editor_1.ADDITIONAL_SHIFTING_TO_Y; | ||
const yPosition = await this.getLineYCoordinates(line, fileName) + Editor_1.ADDITIONAL_SHIFTING_TO_Y; | ||
new selenium_webdriver_1.ActionSequence(this.driverHelper.getDriver()). | ||
@@ -345,5 +346,5 @@ keyDown(selenium_webdriver_1.Key.CONTROL). | ||
} | ||
async mouseRightButtonClick(line, char) { | ||
async mouseRightButtonClick(line, char, fileName) { | ||
Logger_1.Logger.debug(`Editor.mouseRightButtonClick line: "${line}" char: "${char}"`); | ||
const yPosition = await this.getLineYCoordinates(line) + Editor_1.ADDITIONAL_SHIFTING_TO_Y; | ||
const yPosition = await this.getLineYCoordinates(line, fileName) + Editor_1.ADDITIONAL_SHIFTING_TO_Y; | ||
new selenium_webdriver_1.ActionSequence(this.driverHelper.getDriver()). | ||
@@ -388,12 +389,12 @@ mouseMove({ x: char + Editor_1.ADDITIONAL_SHIFTING_TO_X, y: yPosition }). | ||
} | ||
async getErrorInLineLocator(lineNumber) { | ||
const lineYCoordinates = await this.getLineYCoordinates(lineNumber); | ||
async getErrorInLineLocator(lineNumber, fileName) { | ||
const lineYCoordinates = await this.getLineYCoordinates(lineNumber, fileName); | ||
return selenium_webdriver_1.By.xpath(`//div[contains(@style, 'top:${lineYCoordinates}px')]//div[contains(@class, 'squiggly-error')]`); | ||
} | ||
async getWarningInLineLocator(lineNumber) { | ||
const lineYCoordinates = await this.getLineYCoordinates(lineNumber); | ||
async getWarningInLineLocator(lineNumber, fileName) { | ||
const lineYCoordinates = await this.getLineYCoordinates(lineNumber, fileName); | ||
return selenium_webdriver_1.By.xpath(`//div[contains(@style, 'top:${lineYCoordinates}px')]//div[contains(@class, 'squiggly-warning')]`); | ||
} | ||
async getInfoInLineLocator(lineNumber) { | ||
const lineYCoordinates = await this.getLineYCoordinates(lineNumber); | ||
async getInfoInLineLocator(lineNumber, fileName) { | ||
const lineYCoordinates = await this.getLineYCoordinates(lineNumber, fileName); | ||
return selenium_webdriver_1.By.xpath(`//div[contains(@style, 'top:${lineYCoordinates}px')]//div[contains(@class, 'squiggly-info')]`); | ||
@@ -400,0 +401,0 @@ } |
@@ -114,3 +114,3 @@ "use strict"; | ||
try { | ||
await editor.waitErrorInLine(30, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.waitErrorInLine(30, javaFileName, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
} | ||
@@ -122,6 +122,6 @@ catch (err) { | ||
await ide.waitIde(); | ||
await editor.waitErrorInLine(30, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT * 2); | ||
await editor.waitErrorInLine(30, javaFileName, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT * 2); | ||
} | ||
await editor.performKeyCombination(javaFileName, selenium_webdriver_1.Key.chord(selenium_webdriver_1.Key.BACK_SPACE)); | ||
await editor.waitErrorInLineDisappearance(30); | ||
await editor.waitErrorInLineDisappearance(30, javaFileName); | ||
}); | ||
@@ -128,0 +128,0 @@ test('Suggestion', async () => { |
@@ -124,3 +124,3 @@ "use strict"; | ||
try { | ||
await editor.waitErrorInLine(30, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.waitErrorInLine(30, javaFileName, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
} | ||
@@ -132,6 +132,6 @@ catch (err) { | ||
await ide.waitIde(); | ||
await editor.waitErrorInLine(30, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT * 2); | ||
await editor.waitErrorInLine(30, javaFileName, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT * 2); | ||
} | ||
await editor.performKeyCombination(javaFileName, selenium_webdriver_1.Key.chord(selenium_webdriver_1.Key.BACK_SPACE)); | ||
await editor.waitErrorInLineDisappearance(30); | ||
await editor.waitErrorInLineDisappearance(30, javaFileName); | ||
}); | ||
@@ -138,0 +138,0 @@ test('Suggestion', async () => { |
@@ -62,5 +62,5 @@ "use strict"; | ||
await editor.type(tabTitle, textForErrorDisplaying, 10); | ||
await editor.waitErrorInLine(10, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.waitErrorInLine(10, tabTitle, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.performKeyCombination(tabTitle, selenium_webdriver_1.Key.chord(selenium_webdriver_1.Key.BACK_SPACE)); | ||
await editor.waitErrorInLineDisappearance(10); | ||
await editor.waitErrorInLineDisappearance(10, tabTitle); | ||
}); | ||
@@ -67,0 +67,0 @@ test('Codenavigation', async () => { |
@@ -59,5 +59,5 @@ "use strict"; | ||
await editor.type(tabTitle, textForErrorDisplaying, 13); | ||
await editor.waitErrorInLine(13, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.waitErrorInLine(13, tabTitle, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.type(tabTitle, selenium_webdriver_1.Key.chord(selenium_webdriver_1.Key.DELETE), 13); | ||
await editor.waitErrorInLineDisappearance(13); | ||
await editor.waitErrorInLineDisappearance(13, tabTitle); | ||
}); | ||
@@ -64,0 +64,0 @@ }); |
@@ -65,5 +65,5 @@ "use strict"; | ||
await editor.type(tabTitle, textForErrorDisplaying, 7); | ||
await editor.waitErrorInLine(7, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.waitErrorInLine(7, tabTitle, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.performKeyCombination(tabTitle, selenium_webdriver_1.Key.chord(selenium_webdriver_1.Key.BACK_SPACE, selenium_webdriver_1.Key.BACK_SPACE, selenium_webdriver_1.Key.BACK_SPACE)); | ||
await editor.waitErrorInLineDisappearance(7); | ||
await editor.waitErrorInLineDisappearance(7, tabTitle); | ||
}); | ||
@@ -70,0 +70,0 @@ }); |
@@ -78,5 +78,5 @@ "use strict"; | ||
await editor.type(tabTitle, textForErrorDisplaying, 5); | ||
await editor.waitErrorInLine(4, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.waitErrorInLine(4, tabTitle, TimeoutConstants_1.TimeoutConstants.TS_ERROR_HIGHLIGHTING_TIMEOUT); | ||
await editor.type(tabTitle, selenium_webdriver_1.Key.chord(selenium_webdriver_1.Key.DELETE, selenium_webdriver_1.Key.DELETE), 5); | ||
await editor.waitErrorInLineDisappearance(4); | ||
await editor.waitErrorInLineDisappearance(4, tabTitle); | ||
}); | ||
@@ -83,0 +83,0 @@ test('Codenavigation', async () => { |
@@ -63,10 +63,10 @@ "use strict"; | ||
await editor.type(fileName, errorText, 4); | ||
await editor.waitErrorInLine(4); | ||
await editor.waitErrorInLine(4, fileName); | ||
}); | ||
test('Check errors highlighting disappearance', async () => { | ||
await editor.type(fileName, selenium_webdriver_1.Key.DELETE, 4); | ||
await editor.waitErrorInLineDisappearance(4); | ||
await editor.waitErrorInLineDisappearance(4, fileName); | ||
}); | ||
test('Check warning highlighting', async () => { | ||
await editor.waitWarningInLine(5); | ||
await editor.waitWarningInLine(5, fileName); | ||
}); | ||
@@ -77,3 +77,3 @@ test('Uncomment the 4-th row', async () => { | ||
test('Check warning highlighting disappearance', async () => { | ||
await editor.waitWarningInLineDisappearance(5); | ||
await editor.waitWarningInLineDisappearance(5, fileName); | ||
}); | ||
@@ -80,0 +80,0 @@ }); |
@@ -57,3 +57,3 @@ "use strict"; | ||
await projectTree.expandPathAndOpenFile(pathToFile, docFileName); | ||
await editor.waitInfoInLine(16); | ||
await editor.waitInfoInLine(16, docFileName); | ||
}); | ||
@@ -60,0 +60,0 @@ test('Open the "Problems" terminal tab', async () => { |
@@ -61,7 +61,7 @@ "use strict"; | ||
await editor.type(xmlFileName, '\$\%\^\#', 16); | ||
await editor.waitErrorInLine(16); | ||
await editor.waitErrorInLine(16, xmlFileName); | ||
}); | ||
test('Check error disappearance', async () => { | ||
await editor.performKeyCombination(xmlFileName, selenium_webdriver_1.Key.chord(selenium_webdriver_1.Key.BACK_SPACE, selenium_webdriver_1.Key.BACK_SPACE, selenium_webdriver_1.Key.BACK_SPACE, selenium_webdriver_1.Key.BACK_SPACE)); | ||
await editor.waitErrorInLineDisappearance(16); | ||
await editor.waitErrorInLineDisappearance(16, xmlFileName); | ||
}); | ||
@@ -68,0 +68,0 @@ test('Check auto-close tags', async () => { |
@@ -66,7 +66,7 @@ "use strict"; | ||
await editor.type(yamlFileName, selenium_webdriver_1.Key.SPACE, 19); | ||
await editor.waitErrorInLine(19); | ||
await editor.waitErrorInLine(19, yamlFileName); | ||
}); | ||
test('Check error disappearance', async () => { | ||
await editor.performKeyCombination(yamlFileName, selenium_webdriver_1.Key.BACK_SPACE); | ||
await editor.waitErrorInLineDisappearance(19); | ||
await editor.waitErrorInLineDisappearance(19, yamlFileName); | ||
}); | ||
@@ -73,0 +73,0 @@ test('To unformat the "yaml" file', async () => { |
@@ -50,3 +50,3 @@ "use strict"; | ||
try { | ||
await this.editor.waitErrorInLine(line); | ||
await this.editor.waitErrorInLine(line, openedTab); | ||
} | ||
@@ -61,3 +61,3 @@ catch (err) { | ||
} | ||
await this.editor.waitErrorInLineDisappearance(line); | ||
await this.editor.waitErrorInLineDisappearance(line, openedTab); | ||
}); | ||
@@ -64,0 +64,0 @@ } |
@@ -87,3 +87,3 @@ "use strict"; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.waitVisibility - failed with exception, out of attempts - ${err}`); | ||
@@ -214,3 +214,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.waitAndClick - failed with exception, out of attempts - ${err}`); | ||
@@ -252,3 +252,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.waitAndGetElementAttribute - failed with exception, out of attempts - ${err}`); | ||
@@ -289,3 +289,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.waitAndGetCssValue - failed with exception, out of attempts - ${err}`); | ||
@@ -333,3 +333,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.type - failed with exception, out of attempts - ${err}`); | ||
@@ -370,3 +370,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.typeToInvisible - failed with exception, out of attempts - ${err}`); | ||
@@ -407,3 +407,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.clear - failed with exception, out of attempts - ${err}`); | ||
@@ -444,3 +444,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.clearInvisible - failed with exception, out of attempts - ${err}`); | ||
@@ -493,3 +493,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.waitAndGetText - failed with exception, out of attempts - ${err}`); | ||
@@ -578,3 +578,3 @@ throw err; | ||
catch (err) { | ||
if (i >= attempts) { | ||
if (i >= attempts - 1) { | ||
Logger_1.Logger.error(`DriverHelper.scrollTo - failed with exception, out of attempts - ${err}`); | ||
@@ -581,0 +581,0 @@ throw err; |
@@ -87,2 +87,22 @@ "use strict"; | ||
} | ||
async deletePublicSshKeyByName(authToken, keyName) { | ||
const gitHubApiSshURL = GitHubUtil_1.GITHUB_API_ENTRIPOINT_URL + 'user/keys'; | ||
const authHeader = { headers: { 'Authorization': 'token ' + authToken, 'Content-Type': 'application/json' } }; | ||
try { | ||
const response = await axios_1.default.get(gitHubApiSshURL, authHeader); | ||
const stringified = JSON.stringify(response.data); | ||
const arrayOfPublicKeys = JSON.parse(stringified); | ||
for (let entry of arrayOfPublicKeys) { | ||
if (entry.title === keyName) { | ||
this.removePublicSshKey(authToken, entry.id); | ||
break; | ||
} | ||
} | ||
} | ||
catch (error) { | ||
console.error('Cannot delete the ' + keyName + ' public key from the GitHub account'); | ||
console.error(error); | ||
throw error; | ||
} | ||
} | ||
async removeAllPublicSshKeys(authToken) { | ||
@@ -89,0 +109,0 @@ try { |
{ | ||
"name": "@eclipse-che/che-e2e", | ||
"version": "7.40.0", | ||
"version": "7.40.1-dev-7e70384", | ||
"description": "", | ||
@@ -52,3 +52,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@eclipse-che/api": "7.38.1", | ||
"@eclipse-che/api": "latest", | ||
"inversify": "5.0.1", | ||
@@ -55,0 +55,0 @@ "reflect-metadata": "0.1.13" |
@@ -15,4 +15,6 @@ | ||
- ```export TS_SELENIUM_BASE_URL=<Che7 URL>``` | ||
- ```npm install``` | ||
- ```npm ci``` | ||
Note: If there is any modifications in package.json, manually execute the `npm install` to update the package-lock.json. So that errors can be avoided while executing npm ci | ||
## Default launch | ||
@@ -19,0 +21,0 @@ |
@@ -76,2 +76,21 @@ import { injectable } from 'inversify'; | ||
async deletePublicSshKeyByName(authToken: string, keyName: string) { | ||
const gitHubApiSshURL: string = GitHubUtil.GITHUB_API_ENTRIPOINT_URL + 'user/keys'; | ||
const authHeader = { headers: { 'Authorization': 'token ' + authToken, 'Content-Type': 'application/json' } }; | ||
try { | ||
const response = await axios.get(gitHubApiSshURL, authHeader); | ||
const stringified = JSON.stringify(response.data); | ||
const arrayOfPublicKeys = JSON.parse(stringified); | ||
for (let entry of arrayOfPublicKeys) { | ||
if (entry.title === keyName) { | ||
this.removePublicSshKey(authToken, entry.id); | ||
break; | ||
} | ||
} | ||
} catch (error) { | ||
console.error('Cannot delete the ' + keyName + ' public key from the GitHub account'); | ||
console.error(error); | ||
throw error; | ||
} | ||
} | ||
@@ -78,0 +97,0 @@ async removeAllPublicSshKeys(authToken: string) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 3 instances in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 8 instances in 1 package
35
-12.5%89
2.3%1495247
-9.9%359
-36.46%20111
-10.65%2
100%+ Added
- Removed
Updated