Create node version of gentest script #1498
@@ -32,6 +32,7 @@ module.exports = {
|
||||
},
|
||||
env: {
|
||||
es2020: true,
|
||||
'shared-node-browser': true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
|
89
gentest/gentest-driver.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
import * as fs from 'node:fs/promises';
|
||||
import {format} from 'node:util';
|
||||
import * as path from 'path';
|
||||
import * as process from 'node:process';
|
||||
import {Builder, logging} from 'selenium-webdriver';
|
||||
import {Options} from 'selenium-webdriver/chrome.js';
|
||||
|
||||
await (async () => {
|
||||
const options = new Options();
|
||||
options.addArguments(
|
||||
'--force-device-scale-factor=1',
|
||||
'--window-position=0,0',
|
||||
'--hide-scrollbars',
|
||||
);
|
||||
options.setLoggingPrefs({
|
||||
browser: 'ALL',
|
||||
performance: 'ALL',
|
||||
});
|
||||
const driver = await new Builder()
|
||||
.forBrowser('chrome')
|
||||
.setChromeOptions(options)
|
||||
.build();
|
||||
|
||||
process.chdir(path.dirname(process.argv[1]));
|
||||
|
||||
const fixtures = await fs.readdir('./fixtures');
|
||||
for (const fileName of fixtures) {
|
||||
![]() It would be nice if the behaviour when the generation script (script run within the browser tab) throws an error could be improved. Specifically, it would be nice if no tests were updated in that case (and ideally that the error, along with the file/fixture that caused it, was printed to the console). It would be nice if the behaviour when the generation script (script run within the browser tab) throws an error could be improved. Specifically, it would be nice if no tests were updated in that case (and ideally that the error, along with the file/fixture that caused it, was printed to the console).
![]() A followup me an @NickGerleman were thinking about was to make it so that there is minimum logic being run on the browser. Right now that is where all the complicated code generation comes from because that is where we inspect the browsers layout. Ideally we port that over to being driven off of the browser if our driver library has such capabilities (not very familiar with all that you can do). Right now, because all of that logic is on the browser and we just read the console.logs, it is hard to do anything elegant around error handling and debugging. A followup me an @NickGerleman were thinking about was to make it so that there is minimum logic being run on the browser. Right now that is where all the complicated code generation comes from because that is where we inspect the browsers layout. Ideally we port that over to being driven off of the browser if our driver library has such capabilities (not very familiar with all that you can do). Right now, because all of that logic is on the browser and we just read the console.logs, it is hard to do anything elegant around error handling and debugging.
|
||||
const fixture = await fs.readFile('fixtures/' + fileName, 'utf8');
|
||||
const fileNameNoExtension = path.parse(fileName).name;
|
||||
console.log('Generate', fileNameNoExtension);
|
||||
|
||||
// TODO: replace this with something more robust than just blindly replacing
|
||||
// start/end in the entire fixture
|
||||
![]() Probably the fixtures should be changed to use Probably the fixtures should be changed to use `inset-inline-start` and `inset-inline-end` instead of left/right? I don't think those existed when these tests were created.
![]() There are the other things like There are the other things like `margin-start` etc. I think we need a pretty wholistic system to replace things like that
|
||||
const ltrFixture = fixture
|
||||
.replaceAll('start', 'left')
|
||||
.replaceAll('end', 'right')
|
||||
.replaceAll('flex-left', 'flex-start')
|
||||
.replaceAll('flex-right', 'flex-end');
|
||||
|
||||
const rtlFixture = fixture
|
||||
.replaceAll('start', 'right')
|
||||
.replaceAll('end', 'left')
|
||||
.replaceAll('flex-right', 'flex-start')
|
||||
.replaceAll('flex-left', 'flex-end');
|
||||
|
||||
const template = await fs.readFile('test-template.html', 'utf8');
|
||||
const f = await fs.open('test.html', 'w');
|
||||
await f.write(
|
||||
format(template, fileNameNoExtension, ltrFixture, rtlFixture, fixture),
|
||||
);
|
||||
await f.close();
|
||||
|
||||
await driver.get('file://' + process.cwd() + '/test.html');
|
||||
const logs = await driver.manage().logs().get(logging.Type.BROWSER);
|
||||
|
||||
await fs.writeFile(
|
||||
`../tests/generated/${fileNameNoExtension}.cpp`,
|
||||
eval(logs[0].message.replace(/^[^"]*/, '')),
|
||||
![]() I believe this could use I believe this could use `JSON.parse` instead of `eval` (which would probably be preferable both from a security and code readability perspective?)
|
||||
);
|
||||
![]() Looks like this doesn't delete old tests where the test fixture has been deleted or renamed? I think that was being handled in the old script by deleting the entire directory prior to writing out the new tests. IMO the best order would be:
Looks like this doesn't delete old tests where the test fixture has been deleted or renamed? I think that was being handled in the old script by deleting the entire directory prior to writing out the new tests. IMO the best order would be:
- Run all fixtures in the browser (keeping results in memory)
- If all run without error:
- Delete existing test directories
- Write out all tests in parallel
![]() ooo good callout. I can take a look at that ooo good callout. I can take a look at that
|
||||
|
||||
await fs.writeFile(
|
||||
`../java/tests/com/facebook/yoga/${fileNameNoExtension}.java`,
|
||||
eval(logs[1].message.replace(/^[^"]*/, '')).replace(
|
||||
'YogaTest',
|
||||
fileNameNoExtension,
|
||||
),
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
`../javascript/tests/generated/${fileNameNoExtension}.test.ts`,
|
||||
eval(logs[2].message.replace(/^[^"]*/, '')).replace(
|
||||
'YogaTest',
|
||||
fileNameNoExtension,
|
||||
),
|
||||
);
|
||||
}
|
||||
await fs.unlink('test.html');
|
||||
await driver.quit();
|
||||
})();
|
14
gentest/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "gentest",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@types/node": "^20.10.3",
|
||||
"@types/selenium-webdriver": "^4.1.21",
|
||||
"babel-register-esm": "^1.2.5",
|
||||
"selenium-webdriver": "^4.16.0"
|
||||
}
|
||||
}
|
14
gentest/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "nodenext",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"moduleResolution": "nodenext",
|
||||
"lib": [
|
||||
"ES2021.String"
|
||||
]
|
||||
},
|
||||
"files": ["gentest-driver.ts"]
|
||||
}
|
This script needs instructions on how to run it (especially given that it's a TS file that won't run with vanilla node):
node --loader babel-register-esm gentest-driver.ts
?). Perhaps this could be included in the package.json as a script. And then the instructions could just benpm run gentest
or similar.Yeah in some of the later commits in this stack I update the readme for instructions on how to get this to work wrt to dependencies. We also have scripts in place:
yarn gentest
andyarn gentest-validate
which abstracts that out that loader.As for chromedriver, according to https://www.selenium.dev/documentation/webdriver/troubleshooting/errors/driver_location/#use-the-latest-version-of-selenium, it appears that selenium now handles those deps for you like playwright. I have not tested this yet though, I'll give it a try today