Skip to content

Commit

Permalink
docs / test: Added tests and documentation for Feature 7253 - Migrati…
Browse files Browse the repository at this point in the history
…ons Javascript output
  • Loading branch information
foxxor committed Jan 19, 2021
1 parent aea2786 commit 7c38b97
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 6 deletions.
21 changes: 19 additions & 2 deletions docs/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Once you have a migration to run on production, you can run them using a CLI com
typeorm migration:run
```

**`typeorm migration:create` and `typeorm migration:generate` will create `.ts` files. The `migration:run` and `migration:revert` commands only work on `.js` files. Thus the typescript files need to be compiled before running the commands.** Alternatively you can use `ts-node` in conjunction with `typeorm` to run `.ts` migration files.
**`typeorm migration:create` and `typeorm migration:generate` will create `.ts` files, unless you use the `o` flag (see more in [Generating migrations](#generating-migrations)). The `migration:run` and `migration:revert` commands only work on `.js` files. Thus the typescript files need to be compiled before running the commands.** Alternatively you can use `ts-node` in conjunction with `typeorm` to run `.ts` migration files.

Example with `ts-node`:
```
Expand Down Expand Up @@ -194,8 +194,25 @@ export class PostRefactoringTIMESTAMP implements MigrationInterface {
}
```

Alternatively you can also output your migrations as Javascript files using the `o` (alias for `--outputJs`) flag. This is useful for Javascript only projects in which TypeScript additional packages are not installed. This command, will generate a new migration file `{TIMESTAMP}-PostRefactoring.js` with the following content:

```javascript
const { MigrationInterface, QueryRunner } = require("typeorm");

module.exports = class PostRefactoringTIMESTAMP {

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`);
}
}
```

See, you don't need to write the queries on your own.
The rule of thumb for generating migrations is that you generate them after "each" change you made to your models. To apply multi-line formatting to your generated migration queries, use the `p` (alias for `--pretty`) flag.
The rule of thumb for generating migrations is that you generate them after **each** change you made to your models. To apply multi-line formatting to your generated migration queries, use the `p` (alias for `--pretty`) flag.

## Connection option
If you need to run/revert your migrations for another connection rather than the default, use the `-c` (alias for `--connection`) and pass the config name as an argument
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/github-issues/7068/issue-7068.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Category} from "./entity/Category";
import {Connection} from "../../../src/connection/Connection";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";

describe.only("github issues > #7068", () => {
describe("github issues > #7068", () => {

let connections: Connection[];
before(async () => connections = await createTestingConnections({
Expand Down
14 changes: 14 additions & 0 deletions test/github-issues/7253/entity/Post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { PrimaryGeneratedColumn, Entity, Column, CreateDateColumn } from "../../../../src";

@Entity()
export class Post {

@PrimaryGeneratedColumn()
id?: number;

@Column()
title: string;

@CreateDateColumn()
readonly createdAt?: Date;
}
140 changes: 140 additions & 0 deletions test/github-issues/7253/issue-7253.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import sinon from "sinon";
import { ConnectionOptions, ConnectionOptionsReader, DatabaseType } from "../../../src";
import { setupTestingConnections, createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils";
import { CommandUtils } from "../../../src/commands/CommandUtils";
import { MigrationGenerateCommand } from "../../../src/commands/MigrationGenerateCommand";
import { MigrationCreateCommand } from "../../../src/commands/MigrationCreateCommand";
import { Post } from "./entity/Post";
import { resultsTemplates } from "./results-templates";

describe("github issues > #7253 Allow migration files to be output in Javascript", () => {
let connectionOptions: ConnectionOptions[];
let createFileStub: sinon.SinonStub;
let timerStub: sinon.SinonFakeTimers;
let getConnectionOptionsStub: sinon.SinonStub;
let migrationGenerateCommand: MigrationGenerateCommand;
let migrationCreateCommand: MigrationCreateCommand;
let connectionOptionsReader: ConnectionOptionsReader;
let baseConnectionOptions: ConnectionOptions;

const enabledDrivers = [
"mysql",
] as DatabaseType[];

// simulate args: `npm run typeorm migration:run -- -n test-migration -d test-directory`
const testHandlerArgs = (options: Record<string, any>) => ({
"$0": "test",
"_": ["test"],
"name": "test-migration",
"dir": "test-directory",
...options
});

before(async () => {
// clean out db from any prior tests in case previous state impacts the generated migrations
const connections = await createTestingConnections({
entities: [],
enabledDrivers
});
await reloadTestingDatabases(connections);
await closeTestingConnections(connections);

connectionOptions = setupTestingConnections({
entities: [Post],
enabledDrivers
});
connectionOptionsReader = new ConnectionOptionsReader();

migrationGenerateCommand = new MigrationGenerateCommand();
migrationCreateCommand = new MigrationCreateCommand();
createFileStub = sinon.stub(CommandUtils, "createFile");

timerStub = sinon.useFakeTimers(1610975184784);
});

after(async () => {
timerStub.restore();
createFileStub.restore();
});

beforeEach(async () => {
baseConnectionOptions = await connectionOptionsReader.get(connectionOptions[0].name as string);
getConnectionOptionsStub = sinon.stub(ConnectionOptionsReader.prototype, "get").resolves({
...baseConnectionOptions,
entities: [Post]
});
});

afterEach(async () => {
getConnectionOptionsStub.restore();
});

it("writes regular empty migration file when no option is passed", async () => {
createFileStub.resetHistory();

await migrationCreateCommand.handler(testHandlerArgs({
"connection": connectionOptions[0].name
}));

// compare against control test strings in results-templates.ts
sinon.assert.calledWith(
createFileStub,
sinon.match(/test-directory.*test-migration.ts/),
sinon.match(resultsTemplates.create.control)
);

getConnectionOptionsStub.restore();
});

it("writes Javascript empty migration file when option is passed", async () => {
createFileStub.resetHistory();

await migrationCreateCommand.handler(testHandlerArgs({
"connection": connectionOptions[0].name,
"outputJs": true
}));

// compare against control test strings in results-templates.ts
sinon.assert.calledWith(
createFileStub,
sinon.match(/test-directory.*test-migration.js/),
sinon.match(resultsTemplates.create.javascript)
);

getConnectionOptionsStub.restore();
});

it("writes regular migration file when no option is passed", async () => {
createFileStub.resetHistory();

await migrationGenerateCommand.handler(testHandlerArgs({
"connection": connectionOptions[0].name
}));

// compare against control test strings in results-templates.ts
sinon.assert.calledWith(
createFileStub,
sinon.match(/test-directory.*test-migration.ts/),
sinon.match(resultsTemplates.generate.control)
);

getConnectionOptionsStub.restore();
});

it("writes Javascript printed file when option is passed", async () => {
createFileStub.resetHistory();

await migrationGenerateCommand.handler(testHandlerArgs({
"connection": connectionOptions[0].name,
"outputJs": true
}));

// compare against "pretty" test strings in results-templates.ts
sinon.assert.calledWith(
createFileStub,
sinon.match(/test-directory.*test-migration.js/),
sinon.match(resultsTemplates.generate.javascript)
);
getConnectionOptionsStub.restore();
});
});
54 changes: 54 additions & 0 deletions test/github-issues/7253/results-templates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
export const resultsTemplates: Record<string, any> = {
create: {
control: `import {MigrationInterface, QueryRunner} from "typeorm";
export class testMigration1610975184784 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
}
public async down(queryRunner: QueryRunner): Promise<void> {
}
}`,
javascript: `const { MigrationInterface, QueryRunner } = require("typeorm");
module.exports = class testMigration1610975184784 {
async up(queryRunner) {
}
async down(queryRunner) {
}
}`
},
generate: {
control: `import {MigrationInterface, QueryRunner} from "typeorm";
export class testMigration1610975184784 implements MigrationInterface {
name = 'testMigration1610975184784'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("CREATE TABLE \`post\` (\`id\` int NOT NULL AUTO_INCREMENT, \`title\` varchar(255) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (\`id\`)) ENGINE=InnoDB");
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query("DROP TABLE \`post\`");
}
}`,
javascript: `const { MigrationInterface, QueryRunner } = require("typeorm");
module.exports = class testMigration1610975184784 {
name = 'testMigration1610975184784'
async up(queryRunner) {
await queryRunner.query("CREATE TABLE \`post\` (\`id\` int NOT NULL AUTO_INCREMENT, \`title\` varchar(255) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (\`id\`)) ENGINE=InnoDB");
}
async down(queryRunner) {
await queryRunner.query("DROP TABLE \`post\`");
}
}`
}
};

0 comments on commit 7c38b97

Please sign in to comment.